Thomas Hafner wrote:
> Run the program and let read it from standard input one line
> consisting of an opening and a closing parenthesis, nothing else:
> ()
> 
> The output will be an empty line. (Well, not very exiting, but I've
> looked for the simpliest example.)

OK, I see the problem now. If you use the same symbol everywhere, this 
happens:

  def document ...
    loop do
      catch(:foo) do
        ...
        multiline do
          ...
          throw(:foo)   <-- invoked when block is yielded to

However, multiline calls multiline_tail, which has its own catch/throw 
pair. If it also uses catch(:foo) then the throw will be caught inside 
there instead.

So I think the solution is easy: use two different symbols, e.g. 
catch(:next_document) in document, and catch(:next_multiline_tail) in 
multiline_tail.

This works for the trivial document you provided. You might want to try 
a more substantial example though :-)

>> Or indeed, what's wrong with using loop { .... next ... } ?
> 
> Just replacing ``throw'' by ``next'' doesn't help in these situations:
> loop { ... multiline { ... next ... } ... }
> loop { ... character { ... next ... } ... }
>                                       ^^^
>                                       executed despite ``next''!
> 
> ``next'' will then quit only the innermost block, that's not enough.

You're right, and realising that 'next' was being executed inside an 
inner block is what put me on the right track. Perl has statement labels 
for this:

  OUTER:
  while(1) {
    ...
    next OUTER
  }

but I don't think Ruby has a direct equivalent.

As to the original question: I think the catch/throw version of the code 
is far, far clearer than callcc. Continuations are strange beasts: they 
are entire execution contexts which can be squirreled away and restarted 
at any point in the future. They are essentially threads, albeit 
non-preemptive.

A program which uses callcc can be very hard to understand. Your example 
isn't, but only when you realise that |proceed| is (a) a local variable, 
(b) reset each time around the loop, and (c) is never passed to any 
other method. This guarantees that previous continuation objects will 
not be invoked later and will be garbage-collected.

But you have to analyse the code very carefully to draw this conclusion, 
whereas a loop plus throw/catch is pretty obvious (I think).

Regards,

Brian.

P.S. If I understand your code right, I think you can simplify multiline 
to:

    def multiline( input, p, &blk )
      '(' == input[p] && multiline_tail( input, p+1, &blk )
    end

or

    def multiline( input, p, &blk )
      multiline_tail( input, p+1, &blk ) if '(' == input[p]
    end

All that a block like

  { |p2,v2| yield p2,v2 }

does is to relay the call back to the original block, so why not just 
yield to the original block directly? You were using the 'return' 
statement too, which has special use within a block, but I think it may 
not be needed here.
-- 
Posted via http://www.ruby-forum.com/.