Iterator using Continuations

Hi,

Im am preparing a small presentation for ruby. Therefore I was searching for a
continuation example. I tried to make a Java like iterator for any ruby class
with an each() method. This is the solution I found:

class Iterator
def initialize(enum)
@enum = enum
@current = nil
@back = nil

@enum.each do |el|
  @current = el
  callcc do |cc|
    @next = cc
    if @back then @back.call else return end
  end
end
@current = nil
@back.call if @back

end

def hasNext?
@current != nil
end

def next
last = @current
callcc do |cc|
@back = cc
@next.call if @current
end
last
end
end

iter = Iterator.new(1…5)
while iter.hasNext?
puts iter.next
end

What do you think of this approach? Is there an easier way to do it?

Best regards,
Hans Jörg Hessmann

What do you think of this approach? Is there an easier way to do it?

Not a easier way, but, since Ruby’s callcc is kind of slow, I think
it’s better to make a thread and connect with queue. Take a look at:
http://www.imasy.or.jp/~fukumoto/ruby/pipe.rb

					FUKUMOTO Atsushi
					fukumoto@imasy.or.jp

You should be able to find some approaches in the ruby-talk archives.
There’s also Knu’s implementation in CVS (look in “rough”). If you
have problems finding them I can look for more details.

Regards,
Pit

···

On 17 Mar 2003 at 3:29, Hans Jörg Hessmann wrote:

Im am preparing a small presentation for ruby. Therefore I was searching for a
continuation example. I tried to make a Java like iterator for any ruby class
with an each() method. This is the solution I found:

class Iterator
(…)

What do you think of this approach? Is there an easier way to do it?

That is a contradiction.

Gavin

···

On Monday, March 17, 2003, 5:29:28 AM, Hans wrote:

I am preparing a small presentation for ruby. Therefore I was
searching for a continuation example.

This looks like a reasonable approach, particularly for an example.

Some comments:

  1. What happens if @enum.each raises an exception? (I think that it
    will appear that the exception is coming from #initialize rather
    than #next, which might not be the desired behavior).
  2. This approach to iterating is very slow. It’s better to make a few
    iterations at a time, so you don’t have to call @next every time
    you want a new element.

See [ruby-talk:35757] and the following thread for lots of links to
other people who have implemented this style of iterator in Ruby.

Paul

···

On Mon, Mar 17, 2003 at 03:29:28AM +0900, Hans Jörg Hessmann wrote:

What do you think of this approach? Is there an easier way to do it?

Some comments:

  1. What happens if @enum.each raises an exception? (I think that it
    will appear that the exception is coming from #initialize rather
    than #next, which might not be the desired behavior).
  2. This approach to iterating is very slow. It’s better to make a few
    iterations at a time, so you don’t have to call @next every time
    you want a new element.

See [ruby-talk:35757] and the following thread for lots of links to
other people who have implemented this style of iterator in Ruby.

Well, I thought it was a good solution that shows an extraordinary
ruby-feature. But I didn’t check the performance. It’s really too slow for
real-world applications.

It looks like there is no better solution than using some sort of pipe.

Anyway, Gavin is right: continuations are nothing for a ruby intoduction. But
I stumbled over this problem during my preparation and it made me curious.

It least I’ve learned something and it was a bit of fun.

Thanks,
Hans Jörg

Paul wrote:

This looks like a reasonable approach, particularly for an example.

I think so too, and add more comments:

  1. If the enumerable object has nil, hasNext returns false.
    It will stop the loop.
    ex) iter = Iterator.new([1, nil, 5])
    while iter.hasNext? #=> stops at nil
    puts iter.next
    end
  2. The symmetry of @back and @next is ignored.

The following is a little improvement of the original code:

class Iterator
def initialize(enum)
@next = nil
@back = nil

enum.each do |el|
  @current = el
  callcc do |cc|
    @next = cc
    if @back then @back.call else return end
  end
end
@next = nil
@back.call if @back

end

def hasNext?
@next
end

def next
last = @current
callcc do |cc|
@back = cc
@next.call if @next
end
last
end
end

As others said, the library using callcc is not a tool for real
applications. But it attracts theoritical interest. I collected
the very short codes for Continuation:

http://blade.nagaokaut.ac.jp/~sinara/ruby/callcc-lib/

It includes the generic library CoThread. Using this, the iterator
can be written as follows:

require “co-thread”
class Iterator
def initialize(enum)
@ct = CoThread.new
@ct.fork do
prev = nil
@more = true
enum.each do |x|
@ct.pass(prev)
prev = x
end
@more = false
@ct.pass(prev)
end
@ct.pass
end

def hasNext?
@more
end

def next
@ct.pass
end
end

Shin-ichiro HARA