Skip to content

Instantly share code, notes, and snippets.

@owensd
Created April 13, 2016 00:51
Show Gist options
  • Save owensd/8c52dfb300ebdd2b5c065ae10869490b to your computer and use it in GitHub Desktop.
Save owensd/8c52dfb300ebdd2b5c065ae10869490b to your computer and use it in GitHub Desktop.
struct LazyTakeWhileGenerator<Base : GeneratorType> : GeneratorType, SequenceType {
var base: Base
var predicate: Base.Element -> Bool
init(base: Base, takeWhile predicate: Base.Element -> Bool) {
self.base = base
self.predicate = predicate
}
mutating func next() -> Base.Element? {
if let n = base.next() where predicate(n) {
return n
}
return nil
}
}
extension LazySequenceType {
typealias ElementType = Self.Elements.Generator.Element
func takeWhile(predicate: ElementType -> Bool) -> LazyTakeWhileSequence<Self.Elements> {
return LazyTakeWhileSequence(base: self.elements, takeWhile: predicate)
}
}
struct LazyTakeWhileSequence<Base : SequenceType> : LazySequenceType {
let base: Base
let predicate: Base.Generator.Element -> Bool
init(base: Base, takeWhile predicate: Base.Generator.Element -> Bool) {
self.base = base
self.predicate = predicate
}
func generate() -> LazyTakeWhileGenerator<Base.Generator> {
return LazyTakeWhileGenerator(base: base.generate(), takeWhile: predicate)
}
}
struct StridingSequenceGenerator<Element> : GeneratorType, SequenceType {
let initial: Element
let stride: Element throws -> Element
var current: Element?
init(initial: Element, stride: Element throws -> Element) {
self.initial = initial
self.stride = stride
self.current = initial
}
mutating func next() -> Element? {
defer {
if let c = current {
current = try? stride(c)
}
else {
current = nil
}
}
return current
}
}
struct StridingSequence<Element> : LazySequenceType {
let initial: Element
let stride: Element throws -> Element
init(initial: Element, stride: Element throws -> Element) {
self.initial = initial
self.stride = stride
}
func generate() -> StridingSequenceGenerator<Element> {
return StridingSequenceGenerator(initial: initial, stride: stride)
}
}
func iterate<T>(initial from: T, stride: T throws -> T) -> StridingSequence<T> {
return StridingSequence(initial: from, stride: stride)
}
for n in iterate(initial: Int(1), stride: {$0 * 2}).takeWhile({ $0 < 10 }) {
print("\(n)", terminator: ", ")
}
print()
print("--")
let items = iterate(initial: Int(1), stride: {$0 * 2})
.filter({ $0 != 4})
.takeWhile({ $0 < 10 })
for n in items {
print("\(n)", terminator: ", ")
}
print()
print("--")
for var n = 1; n < 10; n = n * 2 {
print("\(n)", terminator: ", ")
}
@algal
Copy link

algal commented Jul 21, 2016

I'm not saying it's good but there is a much shorter way to get lazy iteration up to a bound by using the existing AnyGenerator type:

var i = 1
let g = AnyGenerator { () -> Int? in
  let val:Int? = (i < 10) ? i : nil
  i = i * 2
  return val
}

Now you can iterate thru the lazily generated sequence using the usual for .. in construct:

for j in g {
  print(i)
}

So this is much less code than you wrote, but it's problems are still pretty obvious: the counter variable i is still in scope after your iterations, the generator g is still in scope after your iteration, and worst of all the closure in the AnyGenerator is not very intuitive

You could also produce your own lazy generator that reproduces the C style for loop somewhat, and this would encapsulate those temporary variables:

struct ForIteration<Element>  : GeneratorType {
  var initial:Element
  let inc:Element->Element
  let cond:Element->Bool

  mutating func next() -> Element? {
    if cond(initial) == false {
      return nil
    }
    else {
      let currentVal = initial
      initial = inc(initial)
      return currentVal
    }
  }
}
extension ForIteration : SequenceType { }

let f = ForIteration<Int>(initial: 1,
                         inc: { $0*2 },
                         cond: { $0 < 10})

for j in f {
  print(j)
}

There are also many situations where you can get lazyness by just adding .lazy to existing sequences, collections, etc..

I agree with your larger point that the Swift standard library should include primitives that cover the case of the C-style loop more intuitively and with equivalent performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment