lispとrubyとpythonと その7 継続とFiber(rubyと少しだけlisp)

頭が継続脳になっていないためかもしれないけど、継続欲しいなー、、、と普段思うことはない。
でもまぁ継続があるのもrubyの特徴なのかなぁ、と思ったので書いてみる。
簡単な例。

require 'continuation'

i = 0
c1 = nil

callcc do|c|
  c1 = c
end

i = i + 1
printf "counter %s\n",i

c1.call if i < 3
#->counter 1
#  counter 2
#  counter 3
#=>true

はじめて継続をさわった時はなくなったはずのスタックが復活するのにビビッたもんだ。

require 'continuation'

$c1 = nil

def test0(arg)
  printf "test0 begin %s\n" ,arg
  test1 arg
  printf "test0 end %s\n" ,arg
end

def test1(arg)
  printf "\ttest1 begin %s\n" ,arg
  test2 arg
  printf "\ttest1 end %s\n" ,arg
end

def test2(arg)
  printf "\t\ttest2 begin %s\n" ,arg
  callcc do |c|
    $c1 = c
  end  
  printf "\t\ttest2 end %s\n" ,arg
end

printf "call test0\n"

flg = nil

test0 "test"

if !flg
  flg = !flg
  printf "call c1\n"
  $c1.call 
end
call test0

#->test0 begin test
#   test1 begin test
#    test2 begin test
#    test2 end test
#   test1 end test
#  test0 end test

call c1
#->  test2 end test
#   test1 end test
#   test0 end test

ついでに1.9から入ったFiberも載せとく。

f = Fiber.new do 
  i = 0
  while(true)
    Fiber.yield i
    i = i + 1
  end
end

puts f.resume
#=>0

puts f.resume
#=>1

puts f.resume
#=>2

common lispにも限定的だけど継続はある。
例えばcl-cont。
スマートで簡単な使い方の例が思いつかないから以下のコードはかなり適当

(require 'cl-interpol)
(cl-interpol:enable-interpol-syntax)

(require 'cl-cont)

(defvar *cont*)

(defun p (a)
  (format t "test ~A ~%" a))

(cl-cont:with-call/cc
  (p (cl-cont:call/cc 
      #'(lambda (k)
   (setf *cont* k)
   (funcall k 0)))))

(funcall *cont* 1)
(funcall *cont* 2)
(funcall *cont* 3)   
#->test 0 
#  test 1 
#  test 2 
#  test 3 
  
;;http://common-lisp.net/project/cl-cont/
;;にも書いてるけどcall/ccよりはlet/ccを使った方がいい(楽)     
(cl-cont:with-call/cc
  (p (cl-cont:let/cc k
       (setf *cont* k)
       (funcall k 0))))

(funcall *cont* 1)
(funcall *cont* 2)
(funcall *cont* 3)
#->test 0 
#  test 1 
#  test 2 
#  test 3 
  
      
(cl-cont:defun/cc f0 (arg)
  (p (cl-cont:let/cc k
       (setf *cont* k)
       (funcall k arg))))

(f0 0)
(funcall *cont* 1)
(funcall *cont* 2)
(funcall *cont* 3)
#->test 0 
#  test 1 
#  test 2 
#  test 3 
  

(funcall 
 (cl-cont:lambda/cc (arg)
   (p (cl-cont:let/cc k
 (setf *cont* k)
 (funcall k arg)))) 0)

(funcall *cont* 1)
(funcall *cont* 2)
(funcall *cont* 3)
#->test 0 
#  test 1 
#  test 2 
#  test 3