Lindaのeval操作
Lindaでタプルを生成する操作にはoutとevalの二つがあります。out操作はRinda::TupleSpaceのwriteに対応します。
0から9までの二乗根のタプルの生成は次のようにします。
/* C-Linda */ for (i = 0; i < 10; i++) out("sqrt", i, sqrt(i));
/* Rinda */ 10.times do |n| ts.write([:sqrt, n, Math.sqrt(n)]) end
eval操作はout操作にそっくりに見えますが、プロセスが生成される点が違います。なんと新しいプロセス側で引数の評価を行い、その結果からタプルを生成します。次の疑似コードは10個のプロセスを生成して、それぞれが一つのタプルを生成するものです。sqrtは生成されたプロセスで計算されます。
/* C-Linda */ for (i = 0; i < 10; i++) eval("sqrt", i, sqrt(i));
ふつうに考えたら、引数の評価をした後にプロセスが生成されそうに見えますが、C-Lindaはライブラリではない(よくしらないけどプリプロセッサか言語の拡張ぽい)のでそんな芸当ができるようです。
Rinda::rinda_eval
eval操作をRubyでポータブルに実装するのはちょっと難しいので、Rinda::rinda_evalはfork()を持つUNIX属だけを対象とすることにしました。このモジュールメソッドは新しいプロセスを生成しブロックを実行します。また、引数のタプルスペースへの参照をブロックへの引数として与えます。ブロックが返したArrayをタプルスペースに追加します。
10.times do |n| Rinda::rinda_eval($ts) do |ts| [:sqrt, n, Math.sqrt(n)] end end
C-Lindaのevalみたいな字面を提供できなかったのは残念ですが、実用的なAPIになったと思います。この例では計算結果のタプルを生成するものですが、ワーカプロセスの生成に使うことが多いと思います。10個のワーカプロセスを生成する例を示します。これは無限ループですけど適当な条件で終了させてもよいですね。
10.times do |n| Rinda::rinda_eval($ts) do |ts| while true _, arg = ts.take([:request, nil]) ts.write([:response, do_it(arg)]) end [:not_reached] end end
MoreRindaのリポジトリにはN-Queenをrinda_evalで解く例があります。
rinda_evalはLindaのeval操作と同じ機能を持つものをちょっと違う字面で提供します。evalを使う教科書の例題を簡単に実行できるようになりますから、この本も読み進めやすいと思います。
最後にsqrtを生成する例の完全な(実行できる)ソースを載せます。
require 'rinda/tuplespace' require 'rinda/eval' place = Rinda::TupleSpace.new DRb.start_service 10.times do |n| Rinda::rinda_eval(place) do |ts| [:sqrt, n, Math.sqrt(n)] end end 10.times do |n| p ts.read([:sqrt, n, nil]) end
MoreRinda、リンクと募集
MoreRindaはgithub上に置いてあります。lib以下をsite_ruby辺りにコピーするだけで使えます。
install.rbが旧いのがそのままになっていたり、gemがなかったりでみなさんには不便をかけてしまうと思います。つきましてはインストーラやgem化をしてくれる人(というかその結果)を募集します。gemは使ってないのでレビューなしで取り込むことになるけども...。
ちなみにMoreRindaにはTokyoCabinetを使った永続版のTupleSpaceなんかも混じってます。
#書いたのはずいぶん前なので記憶があやふや。