From: Dave Lee Date: 2006-02-13T13:51:31+09:00 Subject: Re: FasterGenerator (#66) ------=_Part_3662_14849808.1139806272125 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline This submission: - passes the tests - runs a couple orders faster than the continuation generator using the provided bench - runs the generator block in a separate thread - intended to handle both stateful and stateless (functional) generator blo= cks - provides a Generator::stateless constructor that allows for the generator block to run for more than one callback to Generator#yield - is uncommented thanks, Dave ------=_Part_3662_14849808.1139806272125 Content-Type: application/octet-stream; name=my_generator.rb Content-Transfer-Encoding: 7bit X-Attachment-Id: f_ejmb5sks Content-Disposition: attachment; filename="my_generator.rb" class MyGenerator def self.stateless(enum = nil, &block) g = new(enum, &block) g.instance_variable_set(:@stateless, true) g end def initialize(enum = nil, &block) @index = 0 @stateless = false if enum if enum.respond_to? :to_ary @array = enum.to_ary else block = proc { |g| enum.each { |x| g.yield x } } end elsif block @array = Array.new @block = block [ :current, :end? ].each do |symbol| method = method(symbol) metaclass.define_method(symbol) { |*args| fill_from_block; method.call(*args) } end else raise ArgumentError, 'Generate nothing?' end end def current raise EOFError if spent? @array[@index] end def each(&each_block) if @array @array.each(&each_block) else x = Object.new def x.yield(value); each_block.call(value); end @block.call(x) end self end def end? spent? end attr_reader :index alias_method :pos, :index def next result = current @index += 1 result end def next? not end? end def rewind @index = 0 if @block @array = Array.new if @block @thread = new_fill_thread end self end def yield(value) @array << value Thread.stop unless @stateless self end private def spent? @index >= @array.size end def metaclass class << self public_class_method :define_method self end end def new_fill_thread Thread.new { Thread.stop; @block.call(self) } end def fill_from_block return if not spent? or @block_exhausted @thread ||= new_fill_thread return unless @thread.alive? @thread.wakeup if @stateless @block_exhausted = @thread.join(1).nil? else Thread.pass while spent? and @thread.alive? end @thread.stop unless @thread.stop? end end ------=_Part_3662_14849808.1139806272125--