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を改造しなければならないのでした(どうでもいい報告)。
この手法は、
・外部イテレータを直接作るのが難しくて
・ループによらないアクセスが簡単な
データ構造(木とか) *では* 役に立ちます。
# ライブラリが簡単に書けます。というのはそれほど嬉しいことではない気がする