From: Bob Alexander Date: 2002-11-20T02:59:03+09:00 Subject: Re: ruby-dev summary 18711-18810 This is a multi-part message in MIME format. ------=_NextPart_000_00BD_01C28FB2.9FE80990 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit From: "Pit Capitain" > Do you know implementations like (from Ruby CVS) > rough/lib/generator.rb? Why do you think these are inefficient? I haven't seen that (but I'll definitely take a look). I think generators can be implemented efficiently. In my experience with generators in other languages, the time efficiency is good enough, and depending on the underlying language implementation, they can be costly of space since in some implementations they require their own contiguous stacks (almost like threads -- to support suspended C stacks in the underlying C code). If Ruby has an efficient generator capability, it would be a good way to implement "zip" without the space cost of creating an entire copy of all of the pointers in all involved sequences. BTW, I have quite a bit of experience with languages that support generators, primarily with Icon whose generators are normally used at the site of the generation (like Ruby's iterators) but can be also be captured in a "co-expression" to be used anywhere and any time to grab values from the generator (like Python's generators). Like Matz says, the Ruby iterator mechanism is simpler and nicely handles 99% of the cases that usually come up. On the other hand, in those relatively rare cases where when parallel iteration requirements arise, the "use anywhere" variety of generator can make the task much more pleasant. Maybe Ruby could consider approaches like Icon's where iterators are normally internal (like they are now), but can be converted to external for those occasional needs (apologies if this has already been discussed to death). I attached a small experimental Ruby class I wrote a long time ago that implements something like this. The implementation uses threads and queues, so is not very efficient. However, an API of this sort could possibly be implemented efficiently at a lower level. Here is the description from the top of the code: # # Creates an iterator object from an iterator method. # # Iterator objects do not replace Ruby iterator methods, but they extend # their capabilities in certain ways: # # - The next value(s) can be obtained from anywhere in the program, not # just from the code block passed to the iterator method. # # - Multiple iterator objects can be active in parallel. # # Iterator creation examples: # # i = Iterator.new((1..10).method(:each)) # i = Iterator.new([11, 22, 33].method(:each_with_index)) # # The iterator's values are retrieved as the array of arguments passed # to the iterator method's block, for example: # # list_element, index = i.get # Bob (packing for a trip -- will be away from my email for a couple of weeks) ------=_NextPart_000_00BD_01C28FB2.9FE80990 Content-Type: application/octet-stream; name="iterator.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="iterator.rb" # # Creates an iterator object from an iterator method. # # Iterator objects do not replace Ruby iterator methods, but they extend # their capabilities in certain ways: # # - The next value(s) can be obtained from anywhere in the program, not # just from the code block passed to the iterator method. # # - Multiple iterator objects can be active in parallel. # # Iterator creation examples: # # i =3D Iterator.new((1..10).method(:each)) # i =3D Iterator.new([11, 22, 33].method(:each_with_index)) # # The iterators values are retrieved as the array of arguments passed # to the iterator method's block, for example: # # list_element, index =3D i.get # require 'thread' class Iterator attr_accessor :sentinel # Alternate sentinel object if nil can be = produced # # Initializes a new instance. # # method Bound iterator method # args... Arguments to be passed to the iterator call (default = none) # def initialize(method, *args) @queue =3D SizedQueue.new(1) @done =3D false @sentinel =3D nil Thread.start(method, args) do |method, args| method.call(*args) do |*x| @queue.push(x) end @queue.push(@sentinel) end end # # Gets an array of values passed to the iterator method's block. # Returns "sentinel" after the iteration is finished. # # Returns an array of values passed to the iterator's block. # def get if @done @sentinel else value =3D @queue.pop @done =3D true if value.equal?(@sentinel) value end end # # Test program # def self.test pr =3D self.new((1..10).method(:each)) while values =3D pr.get value, =3D values p value end end end Iterator.test if $0 =3D=3D __FILE__ ------=_NextPart_000_00BD_01C28FB2.9FE80990--