ちまたでは、なにやらフィボナッチ数列が流行っているようなのですが、フィボナッチ数列といえば、以前、Lua について触れたときに、資料で見かけたコルーチンを使ったフィボナッチ数列が印象深かったのに、まだそれを Squeak の Smalltalk で書いていなかったな…と思い出したのでさっそく書いてみました(Continuations のインストール が必要です)。
| continue suspend resume fibGen next | suspend := nil. " for compiler " resume := [:val | [:cont | continue := cont. suspend value: val] callCC]. fibGen := [:aa :bb | [resume value: aa. aa := bb flag: (bb := aa + bb)] repeat]. next := [[:sus | suspend := sus. continue ifNil: [fibGen value: 1 value: 1]. continue value] callCC]. World findATranscript: nil. Transcript clear. 20 timesRepeat: [Transcript cr; show: next value]
…と言っても、id:sumim:20060308:p1 にちょっと手を加えただけ。Smalltalk では、多重代入ができないのが悔しかったので、#flag: なんぞを使って少々トリッキーなこと(二重代入もどき)もしてみました。よい子はマネをしないでネ。
ついでに、ほぼ同じものを Ruby でも。
continue = suspend = nil resume = proc {|val| callcc {|cont| continue = cont; suspend.call(val)} } fib_gen = proc {|inia,inib| a,b = inia,inib; loop {resume.call(a); a,b = b,a+b} } next_val = proc { callcc {|sus| suspend = sus unless continue; fib_gen.call(1,1) end continue.call } } 20.times {puts next_val.call}
追記:
id:rubyco:20060418:fib さんを拝見していて、Ruby でもブロック変数への再代入が可能だと気が付きました。なぜ駄目だと思いこんでいたのでしょうね…。さらに、ブロック変数がテンポラリ変数をオーバーライドするアレな仕様がこの場合はさいわいして、よりシンプルな記述が可能なようです。
continue = suspend = nil resume = proc {|val| callcc {|continue| suspend.call(val)} } fib_gen = proc {|a,b| loop {resume.call(a); a,b = b,a+b} } next_val = proc { callcc {|suspend| unless continue; fib_gen.call(1,1) end continue.call } } 20.times {puts next_val.call}