yojikのlog

yojikのブログです

Rhinoでケイゾク

そろそろ継続の勉強をしたいなと思ってRhinoをダウンロード。最近のRhinoには継続の機能が組み込まれているのだ。Lisp風の構文が苦手な人が継続を勉強したい場合は要チェックだと思う。
今年は、もっとブツを作ろうと思っているので、簡単な解説を書いてみた。自分は継続についてあまりよくわかっていないので、使い方が間違っているかもしれない。何かあればご指摘ください。もっと内容が充実すればオブジェクトの広場に寄稿したいな。

Continuationオブジェクトの使い方

継続の機能は組み込みのContinuationオブジェクトに集約されている。Continuationを作成し、呼び出すだけで利用可能。

  • ある関数内でContuinuationオブジェクトを作成して、適当な変数に代入すればその時点の継続が保存される
    • 多分「その関数を呼び出すまでのコールスタックの情報が保存される」というイメージ修正:そのContinuationを生成した関数を実行してreturnした後に評価すべき式(=継続)がContinuationとして保存されるイメージ。
  • 作成されたContinuationオブジェクトは関数みたいに呼び出しをかけることができる。
    • その瞬間に継続を保存する関数を呼び出した直後(ちとわかりづらい)まで、ジャンプする。
  • Continuationの呼び出し時に引数をつけると、その情報がContinuationを保持していた変数に置き換わる
    • 継続を再開?したときに情報を受け渡すためなんだろうけど、わかりづらかった。いきなり変数の中身が入れ替わってビックリ。

これだけ。ジョジョ紳士の方々にわかりやすく説明すると、継続は第四部のバイツァ・ダストの能力に似ているッ!。川尻早人役がContinuationオブジェクトというわけ。いやわかりづらいか。

以下のコードはRhino 1.6R2でテストしている。ドキュメントによるとContinuationオブジェクトの仕様は将来変更する可能性があるらしいので注意。ちなみにこのバージョンのRhinoでは起動時にオプション指定しなくても継続の機能を利用可能になっている。*1

var top = new Continuation();   //この一連のスクリプトを実行しているトップレベルの関数(それは何だろう?)を
                                //呼び出した後に実行すること(継続)を保存する 
                                //実際には関数内から脱出するために実行する
var current;                    //関数内の継続を保持する変数を宣言        
function capture() {
  current = new Continuation(); //このcapture関数を呼び出した後にやる事(=継続)をcurrentに保存した
  top();                        //関数から脱出する
}
function foo() {
  while(true) {
    print("haru");
    capture(); //capture()内でcurrentに継続(次の行以降にやってること)を保存する 
               //capture()内ではtop()が呼び出されているため、結果fooから脱出することになる
    print("natu");
    capture();
    print("aki");
    capture();
    print("fuyu");
    capture();
  }
}
//呼び出しコード
js> foo()
haru
js> current() //currentに保持した継続を呼び出している
natu
js> current()
aki
js> current()
fuyu
js> current()
haru

capture関数ではcapture関数を呼び出したところまでのコールスタックをcurrentに保存し、トップレベルの継続にジャンプしている。foo()内でcaptureを呼び出すと、この処理をおこなってfoo()から途中脱出する。その後、current()として呼び出すと、captureを呼び出した直後から再開して次のcapture()まで実行する。
ここまで書いておいてcurrentの変数名をnextとしておけばよかった、とちょっと後悔。名前重要!

所感

foo()関数は、春、夏、秋、冬という状態を遷移する、ある種のステートマシーンになっている。この場合ステートを継続に保持していることになる。継続によって、ステートマシーンを必要とするビジネスロジックのフローを自然に記述できそう。ちょっとDSL的な表記も可能だと思う。これはパワフルな機能ですよ奥さん!
↓こちらは(厳密には継続では無く)Pythonのジェネレータの例だけど、その手の考え方がわかる記事。
ジェネレーターによるステート・マシン
それにしてもjar一つで手軽に扱えるRhinoはいいねー。Java6に組み込まれるし、勉強しておいて損は無い感じ。

*1:昔はオプション指定が必要だったみたい