4.18.6. Příklady

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"
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 .