循環を使った FizzBuzz を Squeak Smalltalk で
この方針では以前も書いたような気がしますが、Squeak Smalltalk で無理なく書くとこんな感じになりますか。
| fizz buzz | fizz := [:n | #('' '' 'Fizz') atWrap: n]. buzz := [:n | #('' '' '' '' 'Buzz') atWrap: n]. (1 to: 100) collect: [:n | (fizz value: n), (buzz value: n) ifEmpty: [n]]
無限列(循環列)とかかっこいい機構はないうえに、Smalltalk は多くの言語にはある fun(n) という関数コールのための構文を持たないため、value: n が続くのがなんだかなーって感じもするので、fizz buzz が関数的に振る舞うのを諦めてしまって、いっそ、このように書いてしまったほうが潔いかもしれません。
| fizz buzz | fizz := #('' '' 'Fizz'). buzz := #('' '' '' '' 'Buzz'). (1 to: 100) collect: [:n | (fizz atWrap: n), (buzz atWrap: n) ifEmpty: [n]]
そこでこんなひと工夫。関数(ブロック)が返す文字列(とは限らないけれどとにかくその結果)を結合(#,)して返す関数(ブロック)を改めて返してくる BlockClosure>>#, を次のように別途定義してみましょう。
BlockClosure >> , other ^[:val | (self value: val), (other value: val)]
これを使うと、元のコードもほんの少しだけシンプルになって、次のように書けるようになるわけですが―
| fizz buzz | fizz := [:n | #('' '' 'Fizz') atWrap: n]. buzz := [:n | #('' '' '' '' 'Buzz') atWrap: n]. (1 to: 100) collect: [:n | (fizz, buzz value: n) ifEmpty: [n]]
ただ、Smalltalk の #, は配列や文字列の結合(#(1 2 3), #(4 5) "=> #(1 2 3 4 5) ". 'abc', 'de' "=> 'abcde' ")であることから、相手が関数であれば個人的には関数の合成を連想してしまうので、これだとちょっと違うような気もします。
一方で、たとえば 7 で割り切れるときの Pezz の追加とかはラクなのでこういう拡張時のコード追加がシンプルになる点に関してはすごく好みではありますね。
| fizz buzz pezz | fizz := [:n | #('' '' 'Fizz') atWrap: n]. buzz := [:n | #('' '' '' '' 'Buzz') atWrap: n]. pezz := [:n | #('' '' '' '' '' '' 'Pezz') atWrap: n]. (1 to: 105) collect: [:n | (fizz, buzz, pezz value: n) ifEmpty: [n]]