On Swift-Users yesterday, someone asked how to guard against an empty sequence. The context of the question was how to test a sequence against a predicate and the problem was that an empty sequence returned true for satisfying the predicate.
Now, leaving aside the entire question of the base behavior (which I believe is correct since no element in an empty sequence fails to satisfy the predicate), I thought Jeremy Pereira came up with an especially cute solution:
func all(@noescape where predicate: Generator.Element throws -> Bool) rethrows -> Bool { var count = 0 for element in self { guard try predicate(element) else { return false } count += 1 } return count > 0 }
One of the big questions though that grew out of the discussion was “how do you non-destructively test a sequence for being empty?” My answer to this was to use a buffer with look-ahead. Here’s my first shot at this, completely a rough draft and open for any improvement suggestions you might offer.
public struct BufferedSequence<Base : SequenceType>:GeneratorType, SequenceType { internal var _base: Base internal var _generator: Base.Generator public var bufferedElement: Base.Generator.Element? public init(_ base: Base) { _base = base _generator = base.generate() bufferedElement = _generator.next() } public mutating func next() -> Base.Generator.Element? { defer { if bufferedElement != nil { bufferedElement = _generator.next() } } return bufferedElement } public func isEmpty() -> Bool { return bufferedElement == nil } }
Interestingly enough, the most difficult thing in throwing this little thing together was not trying to think about look-ahead but to translate Swift 3 idioms back to Swift 2.2 so I could mess with this in a playground.
How is the transition going for you? Are you all 3’ed up yet? Or are you still holding firm in 2.2 land and writing production code?
3 Comments
Swift 2.2.1 (or whatever the App Store’s got in ‘store’) for production. Eagerly latest snapshot for not-production-ready-yet they’ll-eventually-be-ready personal projects. ^__^
How are you doing the mental switchover? It’s absolutely killing me
I would make all of the internal state private i.e. _base and _generator would be private. bufferedElement would be public private(set). Other than that, this is pretty much as I would write it, except I wouldn’t have thought of the clever use of defer { … }.
The struct has applications beyond merely testing that a sequence is empty non destructively, I think. It could, for example be used to wrap a token stream for a recursive descent parser for an LL(1) grammar.