Skip to content

Instantly share code, notes, and snippets.

@simi
Created May 17, 2022 09:54
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 simi/1d2c631d2069114c4f3f5efa6e2d73a3 to your computer and use it in GitHub Desktop.
Save simi/1d2c631d2069114c4f3f5efa6e2d73a3 to your computer and use it in GitHub Desktop.
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