Pharo(Smalltalk環境)においてFizzBuzzを書くためのwalkthrough
概要
Smalltalkという名前を聞いたことがある人はいるけれども、実際に触ったことのある人は少ないのではないだろうか。そういう人のために、今回はPharoを利用してFizzBuzzを書くことによって、文法と環境のひとめぐりしたことをメモしようと思う。
はじめに
そういえば、世間的にはゴールデンウィークだけれども、そんなこと関係無いなと思いつつ、手元にある『Squeak入門』を読んだりしていた(たぶん、数あるプログラミング入門書の中で刺激的なものの一つであろうと僕は考えている)。
個人的にはSmalltalkは、プログラミング言語というよりも「プログラミング環境」に近く、そのプログラミングで遊ぶ過程については魅力がありながらも、実際に独力で書いてみるまでには至らなかった。
だが、今回は、自分で独力ながらにFizzBuzzを書いてみることによって、そのあたりを抑えていきたいなと思ったのが、このブログの経路である。Pharoのインストールについては、こちらを参考にして欲しい。
Pharoについて
Smalltalkの環境といえば、Squeakが有名だが、Pharoは、数あるSmalltalkの環境の中でも開発が活発であり、なお自身のドキュメントをできるだけ発信している環境の一つである。
通常、プログラミング言語といえば、黒い画面と詳するウィンドウに結果を表示させるといった地味なものであるが、Pharo(や、他の環境)は、どちらかといえば、独自の仮想マシンを持つ形となっている。例えば、下のような形で。
上記入門書では、Squeakは「システムブラウザ、ワークスペース、トランスクリプト」という三つを使ってプログラミングしていくのが普通であるという。Pharoもおそらくはそのあたりは変わらない。
Pharoにおいては、実行したい箇所にラインを引き、そこを右クリックするとPrint it
とDo it
が表示される。これで簡単に該当コードを実行することができる。
反復を書く(そして、そいつはオブジェクトである)
ここでは、文法(といってもそれほど多くないのが、Smalltalkの特徴でもあると思う)をFizzBuzzを書けるように最短でメモしていく。
自分は、だいたい未知の言語でFizzBuzzを書く場合、最初に如何にして「1から100の文字を出力するか」を調べる。Pharoの場合は、次のように書ける。
Transcript clear. 1 to: 100 do: [ :i | Transcript show: i asString; cr].
この文法を考察すると、次のようになる。
まず、基本的にSmalltalkでは、全ての基本データ型は、オブジェクトであり、メソッドを持っている(既にRubyを知っている人なら、馴染みのある考えだろう)。例えば、上ならば1 to: 100
の部分だろう。
Smalltalkではどうもオブジェクトに対してメッセージを送るという形で表現される。メッセージを受けとると、そのメッセージと同じメソッドが呼びだされる形となる。例えば、Transcrpit clear.
という部分がそうで、これは単項メッセージと呼ばれている。
また、メッセージに対して引数みたいなものを渡す場合、to: x do:
といったように、anyDoing:
といったコロンを付ける。
ちなみに、(1 to: 100) asClass.
とすると、一旦1 to: 100
を実行したあとに、そのクラスを見ることができる。1 to: 100
ならInterval
というクラスであることがわかる。
ブロック
さて、これに対して、[]
で囲まれた文を渡している。これはブロックと呼ばれるものだ。例えば、こいつにもクラスがある。[1 + 2] inspect.
というメッセージを渡すと、以下のようなインスペクターが開く。
このように、Pharo上(そして他のSmalltalk環境?)では、このようにあるインスタンスの中身を見ることができる。
さて、このブロックに対して実行する場合、[1 + 2] value.
とすると良い。
とすると、Interval
のdo:
ほうは、どうなっているのかが気になるところだろう。これは、インスペクターで実際のソースが見られるようになっている(なんて素敵な!)。
do: aBlock "(コメント略)" | aValue index size | index := 0. size := self size. [index < size] whileTrue: [aValue := start + (index * step). index := index + 1. aBlock value: aValue]
||
は、そのメソッド内部での一時変数となる。ブロックに対して、value:
というメッセージでわたしていることがわかる。
条件
ここまで書いて、もう一つのパーツである条件文を書きたいと思う。条件文は、True
とFalse
というクラスに対して、ifTrue:
というメッセージを渡すという、一見奇妙な方法によって実現されている。
これは、最初見たとき、面をくらってしまった。変な話、分岐なんていうものは、メソッド呼び出しで実現できるということだ(もっとも、これは特殊なことではなく、『アンダースタンディング コンピュテーション ―単純な機械から不可能なプログラムまで』では、ラムダ式によってif式
が作れることが示唆されている)。
なので、FizzBuzzを書く場合には、下のように書くことができる。
((1 to: 100) collect: [ :i | (i % 15 == 0) ifTrue:'FizzBuzz' ifFalse: [(i % 5 == 0) ifTrue: 'Buzz' ifFalse: [(i % 3 == 0) ifTrue: 'Fizz' ifFalse: i asString ]]]) do: [:i | Transcript show: i asString; cr].
ちなみに、先ほどのwhileTrue
の場合、ブロックに対してメッセージが送られるようにしている理由は、上記の本によれば、一回のブロックが実行されるたびに、条件式をチェックできるようにしているためだという。
あと、上のFizzBuzzにおいては、cr;
が使われている。これはカスケードと呼ばれるもので、同じオブジェクトに対し、違ったメッセージを送るときに、省略するための文法である。
これはエレガントではない
とはいえ、これを書いてみて、凄く不安になってくる。これは明らかにエレガントではない。もうすこしFizzBuzzについて考察してみよう。
まず、FizzBuzzについてだが、これは「任意の条件文に当てはまるなら文字列を、あてはまらなければ自分の数字を渡す」と言いかえることができる。
(1 to: 100) collect: [ :i | ((i % 15 == 0) ifTrue: 'FizzBuzz' ifFalse: [(i % 5 == 0) ifTrue: 'Buzz' ifFalse: [(i % 3 == 0) ifTrue: 'Fizz']]) ifNil: i].
さて、このシンボル同士は,
で連結することができる。そこで、FizzBuzz
自体は3の倍数と5の倍数のフラグで表現できることを利用し、Integer
に対して、asFizz
とasBuzz
のメソッドを生やしてみることにする。
ここで、おもむろに100
を選択し、インスペクターから、Integer
を右クリックする。
これでブラウザが開くので、任意のメソッドをクリックし、カテゴリを選択する。
今回は、printing
が妥当かと思ったので、それをクリックすると、テンプレートが表示される。
asFizz ^ (self % 3 == 0) ifTrue: 'Fizz' ifFalse: ''
^
は値を返すという意味になる。これで、Accept
を押すとメソッドが生えてくる形となる。
同じ要領で、asBuzz
も書き、コードをリファクタする。
((1 to: 100) collect: [ :i | |fizzbuzz| fizzbuzz := (i asFizz), (i asBuzz). (fizzbuzz size == 0) ifTrue: i ifFalse: fizzbuzz]) do: [:i | Transcript show: i asString; cr].
Smalltalkは左から右に、評価をしていく形になるので、結合するときに評価の順番を変えていきたいときには、()
で括る必要がある。
さて、ifTrue
とifFalse
の部分が多少冗長な感じがあるので、ifSizeZero
というメソッドを作ってみる(もしかしたら、似たようなメソッドがあるのかもしれないけど)。
ifSizeZero: aBlock ^ (self size == 0) ifTrue: aBlock ifFalse: self
さらにリファクタリングをする:
((1 to: 100) collect: [ :i | |fizzbuzz| fizzbuzz := (i asFizz), (i asBuzz). fizzbuzz ifSizeZero: i]) do: [:i | Transcript show: i asString; cr].
あとひといき。これに対して、Integer
にasFizzBuzz
というメソッドを作ることにする。
asFizzBuzz |fizzbuzz| fizzbuzz := (self asFizz), (self asBuzz). ^ fizzbuzz ifSizeZero: self
さて、これで終了である:
((1 to: 100) collect: [:i | i asFizzBuzz]) do: [:i | Transcript show: i asString; cr].
お疲れさまでした。
感想
Smalltalkに関しては、何度も触る機会があったのにも関わらず、自分の怠惰によってスルーしていた。こうやって改めて触ってみると、如何にSmalltalkが環境と一体になっているのかが良くわかった気がした。
とはいえ、このようなFizzBuzzを作るのはSmalltalkらしくない気もした。というのは、Smalltalkらしさというのは、以前触った限りにおいては、GUIなどのアプリケーションをダイレクトに作っていく面白さにあるような気がするので、そのあたりもちゃんとわかったら、メモしていきたい。
だが、Smalltalkの環境自体がとても刺激的であることは間違いない。Rubyに強く影響をあたえているのも納得する。
Smalltalkで業務をする機会はおそらくないだろうけれども、刺激的なプログラミング体験を望むなら、これほど良いものは無いと思う。参考にさせて頂いた『Squeak入門』も名著であるので、機会があれば手にとって欲しいと思う。

- 作者: Mark J. Guzdial,Kimberly M. Rose,軋音組
- 出版社/メーカー: エスアイビーアクセス
- 発売日: 2003/03
- メディア: 単行本
- 購入: 1人 クリック: 40回
- この商品を含むブログ (16件) を見る