Created
May 17, 2022 09:54
-
-
Save simi/1d2c631d2069114c4f3f5efa6e2d73a3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MyEnumerator | |
include Enumerable | |
attr_writer :args | |
attr_writer :size | |
attr_reader :generator | |
attr_reader :lookahead | |
def initialize(&block) | |
receiver = Generator.new(&block) | |
size = nil | |
initialize_enumerator(receiver, size) | |
end | |
def initialize_enumerator(receiver, size, method_name=:each) | |
@object = receiver | |
@size = size | |
@iter = method_name | |
@generator = nil | |
@lookahead = [] | |
@feedvalue = nil | |
self | |
end | |
def next | |
return @lookahead.shift unless @lookahead.empty? | |
@generator ||= Iterator.new self | |
begin | |
return @generator.next if @generator.next? | |
rescue StopIteration | |
end | |
exception = StopIteration.new "iteration reached end" | |
raise exception | |
end | |
def each(*args) | |
enumerator = self | |
new_args = @args | |
unless args.empty? | |
enumerator = dup | |
new_args = @args.empty? ? args : (@args + args) | |
end | |
enumerator.args = new_args | |
if block_given? | |
enumerator.each_with_block do |*yield_args| | |
yield(*yield_args) | |
end | |
else | |
enumerator | |
end | |
end | |
def each_with_block | |
@object.__send__ @iter, *@args do |*args| | |
ret = yield(*args) | |
unless @feedvalue.nil? | |
ret = @feedvalue | |
@feedvalue = nil | |
end | |
ret | |
end | |
end | |
class Generator | |
include Enumerable | |
def initialize(&block) | |
raise LocalJumpError, "Expected a block to be given" unless block_given? | |
@proc = block | |
self | |
end | |
private :initialize | |
def each(*args) | |
enclosed_yield = Proc.new { |*enclosed_args| yield *enclosed_args } | |
@proc.call Yielder.new(&enclosed_yield), *args | |
end | |
end | |
class Yielder | |
def initialize(&block) | |
raise LocalJumpError, "Expected a block to be given" unless block_given? | |
@proc = block | |
self | |
end | |
private :initialize | |
def yield(*args) | |
@proc.call *args | |
end | |
def <<(*args) | |
self.yield(*args) | |
self | |
end | |
end | |
class Iterator | |
attr_reader :result | |
def initialize(obj) | |
@object = obj | |
rewind | |
end | |
private :initialize | |
def next? | |
!@done | |
end | |
def next | |
reset unless @fiber | |
val = @fiber.resume | |
raise StopIteration, "iteration has ended" if @done | |
return val | |
end | |
def rewind | |
@fiber = nil | |
@done = false | |
end | |
def reset | |
@done = false | |
@fiber = Fiber.new do | |
obj = @object | |
@result = obj.each { |*val| Fiber.yield *val } | |
@done = true | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment