『クロージャによる超軽量並行プロセス』を Squeak Smalltalk で
jijixi さんや rubyco さんの Ruby 版とは少し趣を変えて、クラスではなくクロージャ(正確にはブロック…)で。newc はチャンネルを生成するブロックとして記述し、send、recv については、ブロックで表現したチャンネルを評価(#value:value: コール)時に第一引数のシンボル(#send、#recv)として受け取らせ、適宜、処理を切り替えています。
| newc repeat servc serv r fibc fib nn c | newc := [ | senders receivers | senders := OrderedCollection new. receivers := OrderedCollection new. [:sym :x | sym caseOf: { [#send] -> [receivers ifEmpty: [senders add: x] ifNotEmpty: [receivers removeFirst fixTemps valueWithArguments: x]]. [#recv] -> [senders ifEmpty: [receivers add: x] ifNotEmpty: [x fixTemps valueWithArguments: senders removeFirst]] } ] fixTemps ]. World findATranscript: nil. Transcript clear. "整数を受信して画面に表示" c := newc value. c value: #send value: {3}. c value: #recv value: [:x | Transcript cr; show: x]. "=> 3 " "何回でも c から整数を受信して画面に表示" repeat := [ c value: #recv value: [:x | Transcript cr; show: x. repeat value]]. repeat value. (1 to: 3) do: [:i |c copy value: #send value: {i}]. "=> 1 2 3 " "関数サーバー" servc := newc value. serv := [ servc value: #recv value: [:i :recp | recp value: #send value: {i * i}. serv value]]. serv value. r := newc value. servc copy value: #send value: {123. r}. r value: #recv value: [:j | Transcript cr; show: j]. "=> 15129 " servc copy value: #send value: {45. r}. r value: #recv value: [:j | Transcript cr; show: j]. "=> 2025 " "フィボナッチ数列" fibc := newc value. fib := [ fibc value: #recv value: [:n :repc | fib value. n <= 1 ifTrue: [ repc value: #send value: {n}] ifFalse: [ | repc1 repc2 | repc1 := newc value. repc2 := newc value. fibc copy value: #send value: {n - 1. repc1}. fibc copy value: #send value: {n - 2. repc2}. repc1 value: #recv value: [:rep1 | repc2 value: #recv value: [:rep2 | repc value: #send value: {rep1 + rep2}]]]]]. fib value. nn := 10. r := newc value. fibc copy value: #send value: {nn. r}. r value: #recv value: [:m | Transcript cr; show: ('fib({1}) = {2}' format: {nn. m})]. "=> fib(10) = 55 "
ほぼ同じものを Ruby でも。
newc = proc do senders = Array.new receivers = Array.new proc do |sym,*x| case sym when :send if receivers.empty? senders << x else receivers.shift.call(*x) end when :recv if senders.empty? receivers << x.first else x.first.call(*senders.shift) end end end end #整数を受信して画面に表示 c = newc.call c.call(:send, 3) c.call(:recv, proc{ |x| p x }) #=> 3 #何回でも c から整数を受信して画面に表示 rep = proc{ c.call(:recv, proc{ |x| p x; rep.call }) } rep.call (1..3).each{ |i| c.call(:send, i) } #=> 1 # 2 # 3 #関数サーバー servc = newc.call serv = proc do servc.call(:recv, proc do |i,recp| recp.call(:send, i * i) serv.call end) end serv.call r = newc.call servc.call(:send, 123, r) r.call(:recv, proc{ |j| p j }) #=> 15129 servc.call(:send, 45, r) r.call(:recv, proc{ |j| p j }) #=> 2025 #フィボナッチ数列 fibc = newc.call fib = proc do fibc.call(:recv, proc do |n,repc| fib.call if n <= 1 repc.call(:send, n) else repc1 = newc.call repc2 = newc.call fibc.call(:send, n - 1, repc1) fibc.call(:send, n - 2, repc2) repc1.call(:recv, proc do |rep1| repc2.call(:recv, proc do |rep2| repc.call(:send, rep1 + rep2) end) end) end end) end fib.call nn = 10 r = newc.call fibc.call(:send, nn, r) r.call(:recv, proc{ |m| print "fib(#{nn}) = #{m}\n" }) #=> fib(10) = 55
id:sumim:20070618:p2 に続く