Skip to content

Instantly share code, notes, and snippets.

@isaiah
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isaiah/3ae573bdfeb75bdd5788 to your computer and use it in GitHub Desktop.
Save isaiah/3ae573bdfeb75bdd5788 to your computer and use it in GitHub Desktop.
Pure ruby implementation of lazy enumerator
class Enumerator
class Lazy
def initialize(coll)
@lazy = Yid.new do |yielder|
if block_given?
coll.each do |x|
yield yielder, x
end
else
coll.each do |x|
yielder.yield x
end
end
end
end
def each
loop { yield @lazy.next }
rescue StopIteration
end
def select
Lazy.new(self) do |yielder, *vals|
result = yield(*vals)
yielder.yield(*vals) if result
end
end
def map
Lazy.new(self) do |yielder, *vals|
yielder.yield yield(*vals)
end
end
def take(n)
i = 0
Lazy.new(self) do |yielder, *vals|
if i < n
i += 1
yielder.yield(*vals)
else
raise StopIteration
end
end
end
def drop(n)
i = 0
Lazy.new(self) do |yielder, *vals|
if i < n
i += 1
else
yielder.yield(*vals)
end
end
end
def drop_while
to_drop = true
Lazy.new(self) do |yielder, *vals|
if to_drop
result = yield(*vals)
if !result
to_drop = false
end
end
if !to_drop
yielder.yield(*vals)
end
end
end
def next
@lazy.next
end
def force
ret = []
while val = @lazy.next
ret << val
end
rescue StopIteration
ret
end
private
# kudos http://stackoverflow.com/a/19660333/482757
class Yid
def initialize(&block)
# creates a new Fiber to be used as an Yielder
@yielder = Fiber.new do
yield Fiber
raise StopIteration # raise an error if there is no more calls
end
end
def next
# return the value and wait until the next call
@yielder.resume
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment