クロージャによる超軽量並行プロセス
sumiiさんの『クロージャによる超軽量並行プロセス』を、jijixiさんがRubyに移植したようなので、試してみました。jijixiさんが書いたサンプルを写経して(そのあと、変数名などを自分の理解した範囲で書き換えて)やっと理解しました。
require 'picalc' # Syntax sugar class Chan def <<(arg) send(self, arg) end def >>(arg) recv(self, arg) end end # Make ready for server. server_channel = Chan.new server_proc = Proc.new { server_channel >> Proc.new{|x, response_channel| y = x * x # Service response_channel << y server_proc.call } } server_proc.call # Use the server. p_proc = Proc.new{|x| p x} result_channel = Chan.new server_channel << [123, result_channel] result_channel >> p_proc
実行結果は15129です。server_channelに123を送り込み、その二乗(15129)を結果としてもらって表示しています。
うーむむむ。この頭の使い方は記憶に残っているぞ。継続とか遅延評価をやったときの頭の使い方だ。「クロージャのキャッチボール」というか、「クロージャの雪合戦」というか。
次に、同じようにフィボナッチサーバも写経+書き換えしてみました。
require 'picalc' # Syntax sugar class Chan def <<(arg) send(self, arg) end def >>(arg) recv(self, arg) end end # Fibonacci server. fib_channel = Chan.new fib_proc = Proc.new { fib_channel >> Proc.new{|n, response_channel| fib_proc.call # (A) if n <= 1 response_channel << n else son_channel = Chan.new daughter_channel = Chan.new fib_channel << [n - 1, son_channel] fib_channel << [n - 2, daughter_channel] son_channel >> Proc.new{|x| daughter_channel >> Proc.new{|y| z = x + y response_channel << z } } end # (B) } } fib_proc.call n = 10 result_channel = Chan.new fib_channel << [n, result_channel] result_channel >> Proc.new{|x| printf("fib(#{n}) = %d\n", x) }
疑問:(A)の場所にあるfib_proc.callはここになければいけないのかな?(B)でも動いたが…。うーん、まだ理解していないようだ。
→bellbindさんから論文を紹介して貰いました。感謝。Event-Based Programming without Inversion of Control
追記:
『『クロージャによる超軽量並行プロセス』を Ruby で』をもう少し Ruby っぽくで、<< と >> のメソッドになってた。わーい、うれしい♪