While SE-0045 hasn’t yet been scheduled for review, it’s a proposal I’m really looking forward to. Lily Ballard introduces several standard library functions to support sequence operations. Commonly found in other modern languages like C#, Haskell, and Scala, the proposal includes three new sequence functions (scan(_:combine:)
, takeWhile(_:)
, and dropWhile(_:)
) and a global iterate(_:apply:)
function.
The scan
function successively applies a function across the members of a sequence. It uses a rolling result and the next sequence value to create each new member of the output sequence. So you might, for example, create an ever-updating sum or product:
extension SequenceType { /// Returns an array containing the results of /// /// p.reduce(initial, combine: combine) /// /// for each prefix `p` of `self` in order from shortest to longest, starting /// with the empty prefix and ending with `self`. /// /// For example: /// /// (1..<6).scan(0, combine: +) // [0, 1, 3, 6, 10, 15] /// /// - Complexity: O(N) func scan<T>(initial: T, @noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> [T] }
You can think of scan
as a brother of reduce
, but one that outputs a sequence of intermediate values along the way.
The iterate
function in particular is one that many Swift developers have been clamoring for, as it gives you a simple and obvious way to create mathematical progressions that extend beyond simple “n, n + m, n + 2*m, …” strides:
/// Returns an infinite sequence of lazy applications of `apply` to the /// previous value. For example: /// /// iterate(1, apply: { $0 * 2 }) // yields: 1, 2, 4, 8, 16, 32, 64, ... func iterate<T>(initial: T, apply: T -> T) -> IterateSequence<T>
The two “while” functions respectively return the longest prefix of elements that satisfy a given predicate (takeWhile
), and the corresponding suffix (dropWhile
).
protocol SequenceType { // ... /// Returns a subsequence by skipping elements while `dropElement` returns /// `true` and returning the remainder. func dropWhile(@noescape dropElement: (Self.Generator.Element) throws -> Bool) rethrows -> Self.SubSequence /// Returns a subsequence containing the elements until `takeElement` returns /// `false` and skipping the remainder. func takeWhile(@noescape takeElement: (Self.Generator.Element) throws -> Bool) rethrows -> Self.SubSequence }
Combining iterate
with takeWhile
basically introduces a Swift-er version of the C-style for loop. Here are a couple of examples that show how these functions would look like in practice.
for x in iterate(0.1, apply: { $0 + 2 }) .takeWhile({ $0 < 10 }) { // ... 0.1, 2.1, 4.1, ... }
and
for view in iterate(startingSubview, apply: { $0.superview }) .takeWhile({ $0 != nil }) { // ... view, view.superView, view.superView.superView ... }
Like for loops, each example has a starting value, a closure that lazily updates that value for each iteration, and a sequence generator that produces values until its predicate returns false. While you can convert for loops using Swift’s existing do
and while
statements, both iterate
and takeWhile
provide a natural, fp-style correspondence.
When you add in the new SE-0065 collection and index updates, the upcoming stride function and range operator revisions, and floating point math fixes, all of which are under active proposal development, then pretty much all the C-style for loop inadequacies are addressed in a modern, Swift-native style.
Comments are closed.