vieweditattachhistorytopchangessearchhelp

第07回


■SqueakではじめるSmalltalk入門   第7回


 本連載では、名前はよく耳にしていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、通常のプログラミング言語では構文として用意されているループや条件分岐が、すべてをメッセージ式のみで表わそうとするSmalltalkにおいて、どのように実現されているかを解説します。

(3 > 4) ifTrue: [5] ifFalse: [6]   "=> 6 "


 この式は「3」が「4」より大きければ「5」、そうでなければ「6」を返します。もちろん、3は4より大きくないので、ブロック[6]の評価結果である「6」を返すはずです。この式はprint it(cmd+P)で実際に動作を確認できるので試してみてください。ここで、式「3 > 4」は、レシーバ「3」にメッセージ「> 4」を送って「false」を返します。なお、メッセージ式において、単項メッセージは二項メッセージに、二項メッセージはキーワードメッセージに優先して評価されます。したがって、(3 > 4)の括弧は、ここでは読みやすくするために付けましたが、実際には不要です。

3 > 4 ifTrue: [5] ifFalse: [6]   "=> 6"


と書いても問題なく評価されます。同じくキーワードメッセージに優先される単項メッセージも同じです。

(3 isZero) ifTrue: [5] ifFalse: [6]   "=> 6 「3 isZero」は3が0ならtrue"
3 isZero ifTrue: [5] ifFalse: [6]     "=> 6 括弧は不要"


しかし、

(3 isDivisibleBy: 4) ifTrue: [5] ifFalse: [6]   "=> 6 "


のように、レシーバであるtrueやfalseを産生する式が、キーワードメッセージの場合(あるいは、もっと複雑な式の場合)は括弧は必要です。たとえば上の例で、もしこの括弧がないと、

3 isDivisibleBy: 4 ifTrue: [5] ifFalse: [6]


となり、3への「isDivisibleBy: 4 ifTrue: [5] ifFalse: [6]」というメッセージ送信と解釈されてしまいます。当然、#isDivisibleBy:ifTrue:ifFalse:などというセレクタは登録されていないのでこうしたコードがあっても、例外が生じて処理は中断しますし、そもそも評価時(メソッドならコンパイル時)に警告が出るので、ミスしたことには容易に気付くことができるはずです。

 話を戻します。前式は、(3 > 4)が常にfalseを返すので、

false ifTrue: [5] ifFalse: [6]   "=> 6 "


と書き換えても結果は同じです。レシーバ「false」に送られるメッセージ「ifTrue: [5] ifFalse: [6]」は、#ifTure:ifFalseというセレクタ(メソッド名)と、[5]と[6]という2つのパラメータからなります。同じメッセージを、試しにtrueに送ってみましょう。

true ifTrue: [5] ifFalse: [6]   "=> 5 "


今度はブロック[5]の評価結果である「5」が返ります。

 ここで条件分岐について、もう少し掘り下げて考えてみることにします。メッセージ「ifTrue: [5] ifFalse: [6]」によって起動される#ifTrue:ifFalse:というメソッドの中身はどんなものか、想像してみてください。オーソドックスには、レシーバがtrueなら第1パラメータとして与えられた真時ブロックを、falseなら第2パラメータの偽時ブロックを遅延評価して結果を返値として返す…というような手続きを記述した関数(メソッド)を用意すればよいように思います。しかし、Smalltalkではこれとは別のアプローチをとっています。

 Smalltalkでは真偽値(true、false)もオブジェクトです。これは、それぞれが「ifTrue: [5] ifFalse: [6]」というメッセージを受信できることからもお分かりいただけると思います。trueはクラスTrueの、falseはクラスFalseの、それぞれ唯一のインスタンスです。

 メソッド#ifTrue:ifFalse:は、実は、クラスTrue、クラスFalseに別々に定義されています。Smalltalkプログラマのあいだでは、クラスTrueに定義されているメソッド#ifTrue:ifFalseを表わすのに「True >> #ifTrue:ifFalse」のように書く習慣があるので、これに従うと、#ifTrue:ifFalseという名のメソッドは、True >> #ifTrue:ifFalseと、False >> #ifTrue:ifFalseの2つが存在する…ということになります。

 trueはメッセージ「ifTrue: [5] ifFalse: [6]」を受けると、True >>#ifTrue:ifFalse:を起動し、falseはFalse >> #ifTrue:ifFalse:を起動します。True >> #ifTrue:ifFalse:には、第2パラメータを無視し、第1パラメータとして渡されるブロックを評価した結果を返す手続きが記述されています。False >> #ifTrue:ifFalse:には逆、つまり、第1パラメータを無視し、第2パラメータであるブロックを評価した結果を返す手続きだけが記述されています。実際に、True >> #ifTrue:ifFase:、あるいは、False >>#ifTrue:ifFalse:のソースコードを見て、実際にそうなっているか確認してみましょう。

 適当な場所に「ifTrue:ifFalse:」と入力後、選択してからimplementors of it(cmd+M)します。すると、図に示すような2ペインのウインドウが現われるので、上のペインからTrue ifTrue:ifFalse: {controlling}、あるいは、False ifTrue:ifFalse: {controlling}と書かれた選択肢をクリックして選択すると、下のペインにそれに対応するメソッドのソースを呼び出すことができます。

[fig.A]#ifTrue:ifFalse:メソッドのソースを閲覧するためのウインドウ
Uploaded Image: 07a.png

[fig.B]True >> #ifTrue:ifFalse:のソースコード
Uploaded Image: 07b.png

[fig.C]False >> #ifTrue:ifFalse:のソースコード
Uploaded Image: 07c.png

 Smalltalkのメソッドのソースコードの読み方をここで簡単に説明しておきましょう。太字で書かれた1行目、

ifTrue: trueAlternativeBlock falseAlternativeBlock


は、メッセージパターンと呼ばれるもので、このメソッドを呼び出すためのメッセージの記述方法を示したものです。メソッド名(セレクタ。ここでは#ifTrue:ifFalse:)とパラメータ変数(trueAlternativeBlockとfalseAlternativeBlock)の宣言も兼ねています。2行目と3行目にはコメントとしてこのメソッドの説明が書かれています。

 3行目が、短いですがメソッド本体のソースコードです。「↑」は「^」(ハット)で入力できる記号で、メソッドの終了とそのときの返値を明示的にするときに使います。ちなみに返値を明示的にしないときはself(レシーバ自身)が返値として返ります。なお、Smalltalkには返値のない関数(メソッド)というものはありません。

 #ifTrue:ifFalse:メソッド本体のコードを改めて見ていただくと、受け取ったブロックのうち一方を単純に評価(メッセージ「value」を送信)して値を返しているだけであることが、はっきりと見てとれると思います。条件分岐をメッセージ送信で表現することも特殊なら、その実装方法も特殊で、しかもとてもシンプルにそれを実現していることはたいへん興味深いですね。(実はこの話には“大きな嘘”があります。しかし、コンパイラの挙動やバイトコードに言及する必要があるので、またの機会に…)

 次回は、サンプルプログラムに話を戻して、描いた絵を保存する機能拡張を施します。

このページを編集 (5882 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 1280 times.