vieweditattachhistorytopchangessearchhelp

第06回


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


 本連載では、名前はよく耳にしていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。例として提示した簡単なペイントスクリプトの解説をしていますが、今回は、ループや条件分岐の部分を取り上げます。

 前回の解説では、4行目まででは「pen」という一時変数を用意し、Penのインスタンスをそれに束縛し、描く線の太さと色をいずれも、主にメッセージ送信により実現していることを示しました。どんなオブジェクトに、どのようなメッセージを送って、何をさせるのか…。“メッセージ指向”と呼ぶべきSmalltalkオリジナルの「オブジェクト指向」に身をゆだねるとき、ユーザーは常にこれらのことに意識を向けなければなりません。

"01" | pen |
"02" pen _ Pen new.
"03" pen defaultNib: 3.
"04" pen color: Color red.
"05" [Sensor shiftPressed] whileFalse: [
"06"    | position |
"07"    position _ Sensor peekPosition.
"08"    Sensor redButtonPressed
"09"       ifTrue: [pen goto: position]
"10"       ifFalse: [pen place: position]]


 “メッセージ指向”においては、通常のプログラミング言語では専用の構文を用意するループや条件分岐でも「オブジェクトに対するメッセージ送信」のスタイルで表現することをあくまで貫きます。このプログラムでは5行目から10行目に渡って記述されている「whileFalse: [...]」というメッセージ送信が“ループ”を、8行目から10行目に渡って記述されている「ifTrue: [...]ifFalse: [...]」というメッセージ送信が“条件分岐”を表わす例になっています。

 [ ]で括られる記述は、ブロック(BlockContext)と呼ばれる“処理”をオブジェクト化したもののリテラル式です。[ ]を使っていることからすぐに分かるように、Objective-Cはこの種のオブジェクトやそのリテラル記述はサポートしていません(リテラル記述を代えて同種のオブジェクトを提供する外部ライブラリ自体は存在します)。他方で、LISPなどに精通しておられるかたなら、無名関数(λ式)と同種のものだと言えばピンとくるはずです。

 ブロックについては追って詳しく解説を加えなければならないときが来ると思いますが、ここでは簡単に。[ ]で括られた一連の式はその場では評価されず、処理そのものとしてオブジェクト化され、変数に束縛したり、パラメータとして受け渡すなど第1級のデータとして扱うことが可能になります。たとえば、

3 + 4


という式は、そのまま評価してしまうと結果である「7」になってしまい、それを生み出す過程である「3 + 4」という処理としての性格は失われてしまいますが、

[3 + 4]


とブロックオブジェクトとして記述しておくことで、文字通り「3 + 4」という処理を表わすオブジェクトとして扱うことが可能になります。変数への束縛も可能です。メッセージ「value」を送信することで、中に含まれる処理の遅延評価を行なうことができ、結果の「7」を得ることができます。

| block |
block _ [3 + 4].
block value   "=> 7 "


 ブロックがオブジェクトであり、メッセージの受信者(レシーバ)になることができる具体例は、件のプログラムの5行目にも見つけることができます。ここで、メッセージ「whileFalse: [...]」は、

[Sensor shiftPressed]


というブロックオブジェクトに送信されています。#whileFalse:は、レシーバのブロック内の評価値がfalseの間、パラメータのブロックの評価を繰り返すメソッドです。当然、レシーバの評価値がtrueを返す間だけ繰り返す#whileTrue:というメソッドもあります。ちなみに、論理反転のためのメッセージ「not」を使うことで、5行目は、次のように書き換えても同じ意味になります。

"05"  [Sensor shiftPressed not] whileTrue: [


 Sensorにメッセージを送ることで、我々はその刹那のキーボードやマウスの状態を知ることができます(註)。メッセージ「shiftPressed」の送信では、シフトキーが押されているかどうかを真偽値(true、false)の返値として得ることが可能です。このことから分かるように、#whileFalse:、#whileTrue:を起動するメッセージのレシーバは、ブロックでも、特に真偽値を返すブロックであることが期待されます。たとえば、

[true] whileTrue: []


のような場合、レシーバは評価時に常にtrueを返すので、結果、無限ループになります。無限ループから脱するには、cmd+.(ピリオド)をタイプしてください。

 Sensorはクラスではなく、InputSensorあるいはEventSensorに属するインスタンス(システムを構成するオブジェクトのひとつ)を束縛するグローバル変数です。グローバル変数はいったん定義してしまえば、あとは宣言なしでいつでもどこででも使用できる変数で、大文字から始めるきまりになっています。実は、クラスもこのグローバル変数に束縛されているオブジェクトに過ぎません。

 8行目から10行目までがメッセージ送信によって表現された条件分岐の例です。8行目の、

Sensor redButtonPressed


は、評価の瞬間、マウスボタン(2ボタン以上のマウスなら左ボタン)が押されているかを真偽値で返す式です。この返値であるtrue、もしくはfalseに対して「ifTrue: 真時処理 ifFalse: 偽時処理」というメッセージを送ることで、通常のプログラミング言語における条件分岐をシミュレートしています。レシーバがtrueなら第1パラメータである真時処理ブロックを、falseなら第2パラメータである偽時処理ブロックを評価します。これで、マウスボタンが押されていれば、ペンを下げて移動し(描画)、押されていないときはペンを上げた状態で移動(マウスポインタへの単なる追従)という処理を表わしています。

 この#ifTrue:ifFalseの実装方法はおもしろいので、引き続いて次回、もう少し詳しく解説を加えたいと思います。


註:ただしSensorは、Squeakの前身であるSmalltalk-80時代から引き継がれた、MVCと呼ばれる古いGUIフレームワーク(Cocoaでも、この亜種が採用されていますね)で使われていたグローバル変数で、バックワードコンパチビリティの目的で使用できているに過ぎません。現在のMorphicと呼ばれる新しいGUIフレームワークでは、より高機能なActiveHandを使い、Sensorに依存するコードを組むことは推奨されていません。

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


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

This page has been visited 1190 times.