Line 1: Error: Invalid Blog('by Esehara' )

または私は如何にして心配するのを止めてバグを愛するようになったか

>> Zanmemo

あと何かあれは 「esehara あっと じーめーる」 か @esehara まで

Pharo(Smalltalk環境)においてFizzBuzzを書くためのwalkthrough

概要

Smalltalkという名前を聞いたことがある人はいるけれども、実際に触ったことのある人は少ないのではないだろうか。そういう人のために、今回はPharoを利用してFizzBuzzを書くことによって、文法と環境のひとめぐりしたことをメモしようと思う。

はじめに

そういえば、世間的にはゴールデンウィークだけれども、そんなこと関係無いなと思いつつ、手元にある『Squeak入門』を読んだりしていた(たぶん、数あるプログラミング入門書の中で刺激的なものの一つであろうと僕は考えている)。

個人的にはSmalltalkは、プログラミング言語というよりも「プログラミング環境」に近く、そのプログラミングで遊ぶ過程については魅力がありながらも、実際に独力で書いてみるまでには至らなかった。

だが、今回は、自分で独力ながらにFizzBuzzを書いてみることによって、そのあたりを抑えていきたいなと思ったのが、このブログの経路である。Pharoのインストールについては、こちらを参考にして欲しい。

Pharoについて

Smalltalkの環境といえば、Squeakが有名だが、Pharoは、数あるSmalltalkの環境の中でも開発が活発であり、なお自身のドキュメントをできるだけ発信している環境の一つである。

通常、プログラミング言語といえば、黒い画面と詳するウィンドウに結果を表示させるといった地味なものであるが、Pharo(や、他の環境)は、どちらかといえば、独自の仮想マシンを持つ形となっている。例えば、下のような形で。

f:id:nisemono_san:20160501180148p:plain

上記入門書では、Squeakは「システムブラウザ、ワークスペース、トランスクリプト」という三つを使ってプログラミングしていくのが普通であるという。Pharoもおそらくはそのあたりは変わらない。

Pharoにおいては、実行したい箇所にラインを引き、そこを右クリックするとPrint itDo itが表示される。これで簡単に該当コードを実行することができる。

f:id:nisemono_san:20160501180238p:plain

反復を書く(そして、そいつはオブジェクトである)

ここでは、文法(といってもそれほど多くないのが、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.というメッセージを渡すと、以下のようなインスペクターが開く。

f:id:nisemono_san:20160501180552p:plain

このように、Pharo上(そして他のSmalltalk環境?)では、このようにあるインスタンスの中身を見ることができる。

さて、このブロックに対して実行する場合、[1 + 2] value.とすると良い。

とすると、Intervaldo:ほうは、どうなっているのかが気になるところだろう。これは、インスペクターで実際のソースが見られるようになっている(なんて素敵な!)。

f:id:nisemono_san:20160501180649p:plain

do: aBlock
    "(コメント略)"
    | aValue index size |
    index := 0.
    size := self size.
    [index < size]
        whileTrue: [aValue := start + (index * step).
            index := index + 1.
            aBlock value: aValue]

||は、そのメソッド内部での一時変数となる。ブロックに対して、value:というメッセージでわたしていることがわかる。

条件

ここまで書いて、もう一つのパーツである条件文を書きたいと思う。条件文は、TrueFalseというクラスに対して、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に対して、asFizzasBuzzのメソッドを生やしてみることにする。

ここで、おもむろに100を選択し、インスペクターから、Integerを右クリックする。

f:id:nisemono_san:20160501180426p:plain

これでブラウザが開くので、任意のメソッドをクリックし、カテゴリを選択する。

f:id:nisemono_san:20160501180458p:plain

今回は、printingが妥当かと思ったので、それをクリックすると、テンプレートが表示される。

asFizz
    ^ (self % 3 == 0) ifTrue: 'Fizz' ifFalse: ''

^は値を返すという意味になる。これで、Acceptを押すとメソッドが生えてくる形となる。

f:id:nisemono_san:20160501180524p:plain

同じ要領で、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は左から右に、評価をしていく形になるので、結合するときに評価の順番を変えていきたいときには、()で括る必要がある。

さて、ifTrueifFalseの部分が多少冗長な感じがあるので、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]. 

あとひといき。これに対して、IntegerasFizzBuzzというメソッドを作ることにする。

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入門』も名著であるので、機会があれば手にとって欲しいと思う。

Squeak入門―過去から来た未来のプログラミング環境

Squeak入門―過去から来た未来のプログラミング環境