K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[695] イテレータ(とCPS)
投稿者:NykR
2007/02/20 02:13:25

http://kmaebashi.com/programmer/devlang/regexp.html > Java流の、要素を取り出すだけで強制的にポインタを進めてしまう仕様は、 > ちょっとどうかと思うんですがねえ。 同感です。マージとかやりにくそう。 というわけでどちらか片方にするなら、GoFの方が良いな、と思ってたりするのですが、以下のような関数を用意すれば、GoF方式とJava方式の両方をあまり手間をかけずに作ることができます。(両方欲しい、という訳ではありませんが) function iterator_GoF(foreach_CPS) { this = new_object(); this.first = closure () { this.isDone = closure () { return false; }; foreach_CPS(closure (item, cont) { this.currentItem = closure () { return item; }; this.next = closure () { cont(null); }; }, closure (ret) { this.isDone = closure () { return true; }; }); }; this.first(); return this; } function iterator_Java(foreach_CPS) { this = new_object(); closure () { this.hasNext = closure () { return true; }; foreach_CPS(closure (item, cont) { this.next = closure () { cont(null); return item; }; }, closure (ret) { this.hasNext = closure () { return false; }; }); }(); return this; } function foreach(foreach_CPS, proc) { # ついでに作った foreach_CPS(closure (item, cont) { cont(proc(item)); }, closure (ret) {}); } # foreach_CPSは CPS(continuation passing style, 継続渡し形式)で書かれたforeachです。 # つhttp://www.shiro.dreamhost.com/scheme/docs/cont-j.html これらの関数に対し、コレクション側でforeach_CPSを用意して渡せば それぞれの関数に対応したイテレータが返されます。 例えば以下のような2分木があったとすれば tr = {13,{5,{3,{},{}},{13,{12,{},{}},{}}},{16,{16,{15,{},{}},{}},{17,{},{}}}}; function foreachTree_CPS(tree) { return closure (proc, fin) { closure internalIterate(node, cont) { if (node.size() == 0) { cont(null); } else { internalIterate(node[1], closure (ret) { proc(node[0], closure (ret) { internalIterate(node[2], cont); }); }); } }(tree, fin); }; } という関数を1つ定義するだけで print("foreach:"); foreach(foreachTree_CPS(tr), closure (item) { print(" " + item); }); print("\n"); print("GoF :"); for (i = iterator_GoF(foreachTree_CPS(tr)); !i.isDone(); i.next()) { print(" " + i.currentItem()); } print("\n"); print("Java :"); for (i = iterator_Java(foreachTree_CPS(tr)); i.hasNext();) { print(" " + i.next()); } print("\n"); のように、3種類のイテレータが使えてまあ便利。 でもシーケンシャルなコレクションだとそれほど嬉しくはありません。 CPSではforやwhileがまともに使えないので、例えば配列用のforeach_CPSは function foreachArray_CPS(array) { return closure (proc, fin) { closure loop(index, cont) { if (index == array.size()) { cont(null); } else { proc(array[index], closure (ret) { loop(index + 1, cont); }); } }(0, fin); }; } なんてことになって、要素数が多いとforeachに渡したときにクラッシュするなあ、とか、初期値と変数が遠いなあ、とか。 今の実装だと末尾再帰の最適化は物凄く大変そうですしね。 # でもこの形式のループは個人的にはむしろ欲しかったりします、crowbar ver.0.3.02にcall/ccを付けたので。ちなみに、この時点で末尾再帰の最適化はやりやすくなったのですが、CRB_Objectが増えたので先にGCを改造しなければならないのでした(どうでもいい報告)。 この手法は、 ・外部イテレータを直接作るのが難しくて ・ループによらないアクセスが簡単な データ構造(木とか) *では* 役に立ちます。 # ライブラリが簡単に書けます。というのはそれほど嬉しいことではない気がする
[この投稿を含むスレッドを表示] [この投稿を削除]