ユーザガイドも見れるようになったので

イマントコためしたとこだけ備忘録。gparsのユーザガイドがとりあえず見れるようになった(Usageがtodoだったりしてる)記念として、公開前にサンプルコードをながめていじったメモです。これからユーザガイドとつきあわせて見ていきたいと思います。おもいっきり勘違いしてるとこもあるかもしれませんので…

あ、つなみにgparsはGroovyで並列処理であそべるライブラリです。なにができるかはG*ワークショップの杉浦さんの発表資料をご参考に。ただし今とは仕様がかわってる部分があるのでご注意を。


というわけで、とりあえずフィボナッチ数を求めるメソッドをつかって遊んでみたよ。

//n番目のフィボナッチ数を求める
def fib = {n->
  return (n==0)? 1 : (n==1)? 1 : call(n-1)+call(n-2)
}

まずはAsynchronizer.doParallelでやってみる。40ぐらいだと再帰しまくって結構でかい数(165580141)になるので、時間がかかるよ。

import groovyx.gpars.Asynchronizer

def lst = [3,10,40,2,5]
Asynchronizer.doParallel{
  lst.iterator().eachParallel{
    println "fib($it): "+fib(it)
  }
}

イテレートの順番を待たずに並列で処理されて、計算しおわったものだけがパラパラと表示される。うーんパラレル。これだけでもいろいろとあそべそうな気がする。

つぎにおんなじものをActorでやってみる。

import groovyx.gpars.actor.Actor
import static groovyx.gpars.actor.Actors.actor

def lst = [3,10,40,2,5]
def calc = actor{
  def cnt = 1
  loop{
    react{mes->
      println "fib(${mes.num}): ${mes.ret}"
      reply()
      
      cnt++
      if(cnt>lst.size()) stop()
    }
  }
}.start()

lst.each{n->
  def num = n
  actor{
    def ret = fib(num)
    calc.send([num:num,ret:ret])
    react{
      stop()
    }
  }.start()
  
}

calc.join()

個人的おためしコードなので汚いんですけど、lstの要素だけフィボナッチ数の計算メソッドを動かすActorを作って、表示させるActorに終ったものから結果を投げるようなかんじ。計算結果と動作を止めるためにメッセージをやりとりさせるようにしてます。

メッセージのやりとりはオブジェクトだったらなんでもいけるっぽい。うーん鬼畜(最大級の褒め言葉)!! なのでHashMapのやりとりでこんなのも動かせました。

import groovyx.gpars.actor.Actor
import static groovyx.gpars.actor.Actors.actor

def lst = ['john','mike','bob']

//actorのなかでreactをネストってことはその数だけ受けとるってことですね
def goal = actor{
  loop{
    react{first ->
      println "1st: "+first
      react{second ->
        println "2nd: "+second
        react{third ->
          println "3rd: "+third
          stop()
        }
      }
    }
  }
}.start()

lst.each{n->
  actor{
    goal.send(n)
    stop()
  }.start()
}

goal.join()

println "\n--->8--(cut me)-->8---\n"

//actor同士でメッセージをやりとりするならば
//step:0 Aがメッセージ送信。Bは受信してメッセージに性別を追加してAにメッセージ返信。
//step:1 Aは名前と性別を表示してメッセージに年齢を追加。Bへメッセージ送信後に停止。
//step:2 Bは名前を年齢を表示して停止。
 
def goal2 = actor{
  delegate.metaClass.onTimeout = {-> stop()}
  loop{
    react(3000){mes ->
      switch(mes.step){
        case 0:
          reply([name:mes.name, sex:'male', step:1])
          break;
        case 2:
          println mes.name + " is " + mes.age + " yrs old."
          break
      }
    }
  }
}.start()

lst.each{n->
  actor{
    goal2.send([name:n, step:0])
    react{mes->
      println mes.name+" is "+mes.sex
      
      def age_list = ['john':20, 'mike':30, 'bob':40]
      def age = age_list[mes.name]

      goal2.send([name:mes.name, sex:mes.sex, age:age, step:2])
      stop()
    }
  }.start()
}

goal2.join()

Actor同士でやりとりするメッセージ(HashMap)をどんどん変化させつつやりとりできる。Actorおもろすぎる。ちなみにreactのネストはネストの深さだけメッセージを受けとれるということみたい。

scalaとかclojureみたいに引数パターンマッチングみたいなものがGroovyでは無い(はず)なので、メッセージの処理分岐にはswitch文を使ってます。(好き嫌いを問う問題じゃないんですけど)個人的にはswitch文が好きじゃないので、これをなんとかしたいなと思います。ユーザガイドのサンプルコードでもswitch文をつかってて作法といえば作法なのかもしれませんが、それを作法として片付けちゃうと世の中つまらないので現状もうすこしGroovyっぽいやり方を考えてます。

そう。ぼくらにはクロージャがあるじゃないか!!

というわけで次回へつづく。