Swift Tip: Lazy Infinite Sequences
Sometimes it can be more natural to write your code in a functional way. For example, let's say you want to find the first square number larger than 100. An imperative program is easily written:
var current = 1
while current*current <= 100 {
current += 1
}
result = current*current
In Swift, we also have the option to write this in a more functional way:
let result = (1...)
.lazy
.map { $0 * $0 }
.first(where: { $0 > 100})
The second snippet starts with all possible integers, maps over it to find the squares, and then takes the first element. The lazy
turns it into a lazy collection, and makes sure the map
step is only evaluated for an element when it's really needed. Finally, the first(where:)
repeatedly asks the lazy collection for the next element (evaluating the map
) until it finds an element for which the condition is true.
The functional version is also often easier to change. For example, let's say we want the first ten squares which are larger than 100. The imperative code changes quite drastically:
var current = 1
var results: [Int] = []
while results.count < 10 {
if current*current > 100 {
results.append(current*current)
}
current += 1
}
Whereas the functional code is much more similar to the previous version:
let results = (1...)
.lazy
.map { $0 * $0 }
.filter { $0 > 100 }
Array(results.prefix(10))
Note that the results
variable in the functional version is still lazy: nothing is computed until we turn it into an Array
.
One major advantage of lazy sequences is that the consumer of the sequence has control over how many elements should be iterated over. In the imperative version this needs to be built into the producer, leading to less flexible code.
If you'd like to know more about similar techniques, our Advanced Swift book is a great place to start. Prefer video? The Functional Programming Swift Talk Collection is one of our largest.
If you’d like to watch all our episodes, you can subscribe 😉