rubyco の日記 - コルーチン 2


前のエントリーの続き。


何をいまさら…な感もなくもないですが、Ruby のソースと比べてしまうと、Smalltalk のソース(特にファイルアウトした…)は、プログラムというよりは何かの設定ファイルを見ているようで、じつに読み下しにくく見劣りがしてしまいます。

もちろん言うまでもなく Smalltalk のコードは、専用のシステムブラウザの GUI な支援を受けて、ハイパーテキストでリンクをたどったり戻ったりするがごとく、三次元的(?)に読み書きするのが本来で、そもそも、こうしてベタなテキストとして扱うこと自体あまり想定されていないわけなのですが…。な〜んかクヤシイ。w


そこで、新たなクラスやメソッドを定義せずに、Smalltalk の強力かつお手軽な武器(違)であるブロックのみを用いて、ほぼ同じ処理をもう少し読み下しやすくできないものか、書き換えを試みてみました。

| continue suspend resume generator next |

suspend := nil.   " just for the compiler "

resume := [:val |
   [:cont | continue := cont. suspend value: val] callCC].

generator := [:nn |
   [resume value: nn. nn := nn + 1] repeat].

next := [
   [:sus |
      suspend := sus.
      continue ifNil: [generator value: 0].
      continue value] callCC].

World findATranscript: nil. Transcript clear.   " bring the output window front "

5 timesRepeat: [Transcript cr; show: next value]
0
1
2
3
4

ま、だいぶマシになりましたか。しかし、こうして見ると、あらためて結k(。_・☆\ ベキバキ rubyco さんのサンプルがシンプルでありながらも、なおかつ同時に(題材である コルーチンや call/cc の)特徴を的確に際だたせていることについて感心させられます。

追記

もちろん Smalltalk にたいへんよく似た(ところもある) Ruby のこと。上とまったく同じ内容を同じようにして(つまり、クラスやメソッドを定義せずに、それらを a Proc に代えて)書き下すことも可能です。念のため。

continue = suspend = nil

resume = proc {|val|
   callcc {|continue| suspend.call(val)}
}

generator = proc {|n| 
   loop {resume.call(n); n += 1}
}

next_val = proc {
   callcc {|suspend|
      unless continue; generator.call(0) end
      continue.call
   }
}

5.times {puts next_val.call}