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