『クロージャによる超軽量並行プロセス』を 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