4.18. Pokračování (continuation)

4.18.1. Ukázka
4.18.2. Příklad Producent/Konzument
4.18.3. Kooperující procedury
4.18.4. Resumable functions
4.18.5. Ukázky užití callcc
4.18.6. Příklady

section id="continuation" xreflabel="Pokračování"

Odkazy a zdroje:

ToDo

  1. První úkol.

Co je to pokračování?

Volání s konkrétním pokračováním je metoda Kernelu (jádro Ruby).

callcc {|cont| block } -> anObject

Volání vytváří objekt třídy Continuation který předá asociovanému bloku (block). Zavoláme-li v bloku cont.call způsobí návrat z bloku a předání řízení za blok. Vrácená hodnota buďto hodnota bloku, nebo je to hodnota předaná volání cont.call.

Continuation objects are generated by Kernel#callcc. They hold a return address and execution context, allowing a nonlocal return to the end of the callcc block from anywhere within a program. Continuations are somewhat analogous to a structured version of C's setjmp/longjmp (although they contain more state, so you might consider them closer to threads).

Ruby's continuations allow you to create object representing a place in a Ruby program, and then return to that place at any time (even if it has apparently gone out of scope). continuations can be used to implement complex control strucures, but are typically more useful as ways of confusing people.

Continuations come out of a style of programming called Continuation Passing Style (CPS) where the continuation to a function is explicitly passed as an argument to the function. A continuation is essentially the code that will be executed when the function returns. By explicitly capturing a continuation and passing it as an argument, a normal recursive function can be turned in to a tail recursive function and there are interesting optimizations that can be done at that point. Dan Sugalski has some writeups in his "What the Heck is ... " series at: http://www.sidhe.org/~dan/blog.

Since a continuation is related to a function invocation, when you ask for a continuation object you need to specify which function invocation the continuation is for. callcc addresses this by invoking the block, and passing the continuation of the block's invocation to the block itself. Since callcc "knows" it needs the continuation before the block is invoked, I suspect that it might be easier for the implementor than if the continuation of just *any* function invocation could be grabbed.

# Smple Producer/Consumer
# Usage: count(limit)

def count_task(count, consumer)
    (1..count).each do |i|
        callcc {|cc| consumer.call c, i }
    end
    nil
end

def print_task()
    producer, i = callcc { |cc| return cc }
    print "#{i} "
    callcc { |cc| producer.call }
end

def count(limit)
    count_task(limit, print_task())
    print "\n"
end
Licence Creative Commons
Tento dokument Ruby, jehož autorem je Radek Hnilica, podléhá licenci Creative Commons Uveďte autora-Nevyužívejte dílo komerčně-Zachovejte licenci 3.0 Česká republika .