『クロージャによる超軽量並行プロセス』を Squeak Smalltalk で 3
id:sumim:20070618:p2 の続き。
普通にクラスを使ったオーソドックスなものも書きました。もちろん、送受信には #<< 、#>> を。w
Object subclass: #Channel instanceVariableNames: 'receivers senders' Channel >> initialize senders := OrderedCollection new. receivers := OrderedCollection new Channel >> << args receivers ifEmpty: [senders add: args] ifNotEmpty: [receivers removeFirst valueWithArguments: args] Channel >> >> funcBlock senders ifEmpty: [receivers add: funcBlock] ifNotEmpty: [funcBlock valueWithArguments: senders removeFirst]
Channel class >> example1 | channel | "Channel example1" World findATranscript: nil. channel := Channel new. channel << {3}. channel >> [:x | Transcript cr; show: x] Channel class >> example2 | channel repeat | "Channel example2" World findATranscript: nil. channel := Channel new. repeat := [channel >> [:x | Transcript cr; show: x. repeat value]]. repeat value. (1 to: 3) do: [:i | channel << {i}] Channel class >> example3 | serverCh serverProcess replyCh | "Channel example3" World findATranscript: nil. serverCh := Channel new. serverProcess := [serverCh >> [:x :repCh | repCh << {x * x}. serverProcess value]]. serverProcess value. replyCh := Channel new. #(123 45) do: [:i | serverCh << {i. replyCh}. replyCh >> [:x | Transcript cr; show: x]] Channel class >> example4 | fibCh fibServer replyCh x | "Channel example4" World findATranscript: nil. fibCh := Channel new. fibServer := [ fibCh >> [:n :repCh | fibServer fixTemps value. n < 2 ifTrue: [repCh << {n}] ifFalse: [ | repCh1 repCh2 | fibCh << {n - 1. repCh1 := Channel new}. fibCh << {n - 2. repCh2 := Channel new}. repCh1 >> [:rep1 | repCh2 >> [:rep2 | repCh << {rep1 + rep2}]]]]]. fibServer value. fibCh << {x := 10. replyCh := Channel new}. replyCh >> [:y | Transcript cr; show: ('fib({1}) = {2}' format: {x. y})]
Ruby では、より Smalltalk に近づけるため、ブロックで受け取る版。ただ、これをすると #>> のピリオドが省略できなくなるようです。ブロック付き呼び出しをしない #<< のほうには不要なのですが、見た目を統一するためにつけてあります。
class Channel def initialize @senders = Array.new @receivers = Array.new end def <<(*args) if @receivers.empty? @senders << args else @receivers.shift.call(*args) end end def >>(&funcProc) if @senders.empty? @receivers << funcProc else yield(*@senders.shift) end end end def Channel.example1 ch = Channel.new ch. << 3 ch. >> { |x| p x } end def Channel.example2 ch = Channel.new rep = proc{ ch. >> { |x| p x; rep.call } } rep.call (1..3).each{ |i| ch. << i } end def Channel.example3 serv_ch = Channel.new serv_proc = proc{ serv_ch. >> { |x,repCh| repCh. << x * x; serv_proc.call } } serv_proc.call repl_ch = Channel.new [123, 45].each do |i| serv_ch. << [i, repl_ch] repl_ch. >> { |x| p x } end end def Channel.example4 fib_ch = Channel.new fib_serv = proc do fib_ch. >> { |n,r_ch| fib_serv.call if n < 2 r_ch. << n else fib_ch. << [n - 1, r_ch1 = Channel.new] fib_ch. << [n - 2, r_ch2 = Channel.new] r_ch1. >> { |r1| r_ch2. >> { |r2| r_ch. << r1 + r2 } } end } end fib_serv.call fib_ch. << [x = 10, repl_ch = Channel.new] repl_ch. >> { |y| print "fib(#{x}) = #{y}\n" } end