読者です 読者をやめる 読者になる 読者になる

Cyanの継続

思考実験: returnを関数と思ってみる話 - d.y.d.
http://shinh.skr.jp/m/?date=20090307#p11

Cyanを知ってから継続を勉強したというド素人なんですが、

globalVar = 0

hello = ^():
  globalVar = return
  return(0)

x = hello()
say x
say globalVar(1+x)
say x
say "done."
0
1
done.

トップレベルからの継続(この言い回しが正しいのか分かってない)ではx=hello()のとこだけが得られて

main = ^():
  globalVar = 0
  break = return
  hello = ^():
    globalVar = return
    return(0)

  x = hello()
  say x
  if (x == 5): break("done.");
  globalVar(x+1)
say main()

だと

0
1
2
3
4
5
done.

mainが終わるまでの処理も含まれるってのは、REPLとかでこの方が都合が良いからかなーと思ったんだけどどうなんだろう。
って、マニュアルに「トップレベルまでの残りの計算をする関数」と書いてあった。何も特別扱いってことではないのか。

Gaucheでも試してみたら同じ結果に。

(define cont 0)

;(define (main)
  (define (hello)
    (call/cc
     (lambda (c)
       (set! cont c)
       0)))
  
  (define x (hello))
  (print x)
  (print (cont (+ x 1)))
  (print x)
  (print "done.");)
;(main)
0
1
done.
#t

コメント外すと無限ループ。

検索したらつい先日同じ話が継続の違い - Higepon’s blogにあった。見落としてた…
R6RSはまた話がややこしいらしい。

あと

retret = ^(): return(return)
x = retret()
print x()()()()()()()()()

これで何が起こるかすぐ理解できなかったので整理してみる。
2行目でretretが呼び出された時点での継続は ^(a): x = a みたいな感じ。
return(return)では x = return が評価されて、retretからは戻らず3行目に飛ぶ。
3行目はx()の時点で先のreturnに処理が飛ぶのでprintとか後ろの()()()...は無視。
で、x(1)だったら x = 1 となるけど、引き数を渡さなかった場合どうなるのかというと

cyan> x = callcc^(c): cont = c; 0
 => 0
cyan> cont(10)
 => 10
cyan> x
 => 10
cyan> cont()
 => []
cyan> x
 => []

エラーになりそうな気もするけど[[]]が入るらしい。というわけで最終行ではx = [[]]が実行されてプログラム終了、ってことで良いのかな。

ちなみに引数ナシの継続呼び出しは、gaucheでもエラーにならず#undefとなった。

gosh> (define cont 0)
cont
gosh> (define x (call/cc (lambda (c) (set! cont c) 1)))
x
gosh> (cont 10)
x
gosh> x
10
gosh> (cont)
x
gosh> x
#<undef>

これも過去にどっかで議論されてそう。

限定継続

まったく知らないので僕でもわかる継続と部分継続 - まめめも 見て勉強。
うは、shift, resetの作り方全くわからん。
http://community.schemewiki.org/?composable-continuations-tutorialを見るとcall/ccで定義できちゃうってことなのかな。関係ないけどマウスオーバーすると楽しいなこれ。

おまけ

returnXXXみたいなのはマクロで簡単に出来そう、と思ったらSymbol.internがなかったので無理なのでした。 String.internがあった!(->追記)
return[name]みたいな書き方は、スコープを考慮した実現方法が思い付かない。

追記

コメントより

internはStringにあります

うは、見落としてた、というかSymbol.internじゃおかしいよな確かに。ダメだなー。

mac(mydef)^(name, func):
  _ret := ("return_" + name.to_s()).intern()
  _body := Block.new([`(?_ret := return), *func.body.list])
  _func := Function.new(func.params, _body)
  `def(?name, ?_func)

mydef(findFirstNegative)^(arr):
  arr.foreach^(elem):
    if (elem < 0):
      return_findFirstNegative(elem)
  error("not found")

say findFirstNegative([123, 45, -67, 8, -9])

実行

-67

できたできた