http://merd.net/pixel/language-study/various/callcc/ruby.listing
# dumb examples p callcc{|k| "foo"} # => "foo" p callcc{|k| k.call("foo")} # => "foo" # same as above since # callcc{|k| expr} <=> callcc{|k| k.call(expr)} p callcc{|k| k.call("foo") raise "ignored" } # => "foo" # everything after the call to "k" is ignored p "foo " + callcc{|k| "bar "} + "boo" # => "foo bar boo"
# imperative constructs # "return" def inv(v) callcc{|myreturn| p "doing things" myreturn.call(0) if v == 0 # special case for v = 0 p "otherwise doing other things" 1.0 / v } end # "goto" p "doing things" label_here = nil # creating a label here callcc{|k| label_here = k} p "doing other things" label_here.call p "this won't be reached" # "goto" v.2 def mygoto(continuation) continuation.call(continuation) end p "doing things" label_here = callcc{|k| k} p "doing other things" mygoto(label_here) p "this won't be reached"
# returning a special value (dropping the stack of computations) # return the first i where l[i] == e def listindex(e, l) callcc{|not_found| # using not_found for getting out of listindex # without computing the +1's loop = proc{|l| if l == [] then not_found.call(nil) elsif e == l[0] then 0 else 1 + loop.call(l[1..-1]) end } loop.call(l) } end def product(l) callcc{|mybreak| loop = proc{|l| if l == [] then 1 elsif l[0] == 0 then mybreak.call(0) # using "break" as an optimization to drop the computation else l[0] * loop.call(l[1..-1]) end } loop.call(l) } end
# "delay"ing and coroutines (inspired by http://okmij.org/ftp/Scheme/enumerators-callcc.html) ################################################################################ # first here is an imperative generator (a dumb one) generate = proc{|f| (0 .. 10).each{|i| print "." ; f.call(i) } } # we want to use it functionnally # for this, we generate the list out of the generator def generator2list(generator) l = [] generator.call(proc{|e| l << e}) l end l = generator2list(generate) print "l is #{l}\n" # now, we want to create the list lazily, on demand. # the generator2list above can't do this # here is another version of generator2list that uses a coroutine to create the result def generator2list_(generator) callcc{|k_main| generator.call proc{|e| callcc{|k_reenter| k_main.call [e] + callcc{|k_new_main| k_main = k_new_main k_reenter.call } } } k_main.call [] } end l = generator2list_(generate) print "l is #{l}\n" # the advantage of the callcc version above is that it's easy to generate the list lazily def generator2lazy_list(generator) proc{ callcc{|k_main| generator.call proc{|e| callcc{|k_reenter| k_main.call(e, proc{callcc{|k_new_main| k_main = k_new_main k_reenter.call }}) } } k_main.call nil } } end def lazy_list2list(lz) l = [] while lz = lz.call (e, lz) = lz l << e end l end lz = generator2lazy_list(generate) print "lz is" print " #{lazy_list2list(lz)}\n"
# weird examples p callcc{|mygoto| mystart, mynext, mylast = proc{print "start " ; mygoto.call(mynext)}, proc{print "next " ; mygoto.call(mylast)}, proc{print "last" ; "done"} mystart }.call # => returns "done", displays "start next last"