Shiro Kawai
shiro****@lava*****
2003年 1月 20日 (月) 17:42:24 JST
キーから値へのマップを行うフレームワークに関してはずっと頭の隅に あったのですが、置きっぱなしで埃をかぶっていました。 基本的には議論になっているproceduralなAPIに私も賛成です。 イテレータを用いて(イテレート中の辞書オブジェクトに対する)副作用の ある操作を行うのは、難しい問題だと思います。iteratee内の副作用に より、iteratorが保持している内部状態が意味のないものになる可能性 があるからです。 そのため、例えばCommonLispではハッシュテーブルのiterateeが 使える副作用は現在のキーに対するremhashだけ、としたうえ、 さらにiteratorを取り出す with-hash-table-iterator 構文では iteratorの有効期間をその構文のdynamic extentに明示的に 制限してます。 Gaucheも低レベルではハッシュテーブルの外部iteratorがあるのですが、 それがイテレータループの外に洩れ出すと収拾つかなくなるので、表API では隠してあります。(Cレベルではiteratorをスタックに置くことで extentが限られるんで、多少扱い易いのですが)。 で、Gaucheでやるとしたら、やっぱり一連の議論に上がっているように proceduralなインタフェースにして、かつそのextentを call-with-iteratorのdynamic extentに制限するのが、まあ一番 妥当かなと思います (extentの制限をかけるとcall/ccを使った 内部イテレータから外部イテレータへの転換の技が使えなくなりますが…) dynamic extentへの制限が重要なのは、いくつかの副作用については それが直ちに起こることを保証できないからです。辞書オブジェクトの 実装によっては、副作用は単にキューに貯められていて call-with-iteratrを抜ける直前にコミットされるかもしれません。 (特に、insertはコミット後に効力を持つと決めておかないとまずい ですね)。 他のIssueとしては、 * APIで、渡すprocedureが5つというのはいかにも多すぎますねえ。 ここまで来たらメッセージでディスパッチするクロージャに してしまった方がすっきりするような。 コレクションフレームワークとの連続性はこの際あまり重要では ないでしょう。むしろ辞書フレームワークで良いものが出来たら逆に コレクションフレームワークを合わせちゃったっていいし。 * 中断をどうするか。call/ccでやっても良いのだけれど、どうせ iteratee内でnextを呼ばせるんだったら、いっそのこと継続渡しに しちまっても良いんじゃないか。つまり、現在位置の値を取り出す getメソッドを別に設けて、 nextメソッドを呼ぶこと=続きを 検索、ということにする。nextメソッドを呼ばなかったらそこで iterationはおしまい。 * もしnextメソッド=継続とするなら、いっそのことfoldみたいな インタフェースにして、nextメソッドに値を渡してゆくというのは どうか。iterationがおしまいだったらその値がそのままループから 返される。おしまいで無ければ、iterateeに渡される。 というわけで、こんなのはいかが。 call-with-iterator (dict <dictionary>) iteratee seed keys ... で、iterateeは次のように呼ばれる: iteratee cursor seed ここでcursorはprocedureであり、次のような動作をする: (cursor 'get) 現在のキーと値を返す。 (cursor 'next seed) 次にキーとマッチするエントリがあれば、 iteratteeを呼び出す。その際にseedを 第2引数として渡す。もうマッチするエントリが 無ければseedがcall-with-iteratorの 戻り値となる。 (cursor 'update! value) 現在のエントリの値を変更。 (cursor 'delete!) 現在のエントリを削除。 (cursor 'insert! key value) key value ペアを辞書に追加。 但し、実際の追加はcall-with-iterator を抜ける直前に行われる。追加されたエントリ に対してiterateeが呼ばれることはない。 seedは最初にiterateeが呼ばれる時はcall-with-iteratorの受け取った seedで、2回目以降は cursor 'next メソッドに渡された値。 cursorの呼び出しはcall-with-iteratorのdynamic extent内のみで 有効とする。 --shiro