vieweditattachhistorytopchangessearchhelp

第28回


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


 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。

 すでに耳タコ状態かもしれませんが、Smalltalkではすべてをメッセージ送信により表現します。前回解説したブロック、つまり「手続きをオブジェクトとして扱えるようにしたもの」は、制御構造などの通常の言語では構文として用意されている挙動もメッセージ送信で実現する際に役立てられています。実際の例で確認してみましょう。

▼条件分岐
 これはもう何度も出てきていますので説明は不用ですね。レシーバがtrueあるいはfalse、それぞれの場合に評価したい手続きをパラメータ(引数)として添えたメッセージ送信式になっています。#ifTrue:ifFalse:の二つのパラメータ、つまり手続きは、いずれもブロック変数を持たないブロックでなければいけません。

 名前を尋ねた後、入力文字列の有無を判断して声を掛けてくるスクリプトを示します。

| nameString |
nameString _ FillInTheBlank request: 'your name:'.
nameString isEmpty
   ifTrue: [PopUpMenu inform: 'Nobody knows your name...']
   ifFalse: [PopUpMenu inform: 'Hi, ', nameString, '!']


 FillInTheBlankは、request: aStringというメッセージを受けると、aStringをラベルにした入力欄を表示します。Acceptボタンをクリックしたとき、入力済みの文字列を返値とします。Cancelボタンを押すと''、つまり、長さゼロの文字列が返ります。

 isEmptyは、文字列や配列など、要素を持つオブジェクトに対して送信することで、それらに要素があればfalseを、要素なしならばtrueを返させるメッセージです。

'' isEmpty                  " => true  "
'This is a pen.' isEmpty    " => false "
#() isEmpty                 " => true  "
#('this' is $a 10) isEmpty  " => false "


 PopUpMenuは、inform: aStringというメッセージを受けとることで、aStringというキャプションの付いた、「OK」というメニュー項目をひとつだけ持つポップアップメニューを作って表示します。

 ところで、この制御構造も「式」なので、通常のメッセージ式同様に評価後には「値」を返します。#ifTrue:ifFalse:の場合、レシーバに対応したブロックの評価の結果がその返値です。つまり、レシーバがtrueなら第一パラメータにvalueを送信した結果が、falseなら第二パラメータにvalueを送信した結果が返ります。

 このことを利用して、たとえば、上のスクリプトの場合、#ifTrue:ifFalse:のパラメータで共通するPopUpMenuへのメッセージ送信部分を括りだすことによる、記述の簡素化も可能です。

| nameString |
nameString _ FillInTheBlank request: 'your name:'.
PopUpMenu inform:
   (nameString isEmpty
      ifTrue: ['Nobody knows your name...']
      ifFalse: ['Hi, ', nameString, '!'])


 ここでPopUpMenuは、#ifTrue:ifFalse:の返値をパラメータにした#inform:を起動するメッセージを受信しています。意味や処理の順番は変わりますが、挙動は前のスクリプトとまったく同じです。

 レシーバがtrueあるいはfalseの場合のみ処理を指定する#ifTrue:、#ifFalse:、パラメータの順序が逆の#ifFalse:iFTrue:も用意されています。

▼case-switch文のシミュレート
 SqueakシステムのSmalltalkでは、#caseOf:あるいは#caseOf:otherwise:と独自のカーリーブレイス表記を組み合わせて用いることで、Smalltalk処理系としては珍しいcase-switch表現が実現されています。

('abc' atRandom)
   caseOf: {[$a] -> [1]. [$b] -> [2]. [$c] -> [3]}


 atRandomは、文字列や配列など要素を持つコレクションオブジェクトから、任意の要素を取り出すメッセージです。この式では、atRandom送信によって'abc'から取り出された文字オブジェクト(a Character)の場合によって、1、2、3いずれかあらかじめ決められた数値を返します。

カーリーブレイス「{ }」は、リテラルで表現できない要素を持つ配列を直接作りたいときに用いる表現です。この例では[$a] -> [1]、[$b] -> [2]、[$c] -> [3]という三つの式の評価結果を要素とする配列の生成に使われています。

 一見、特殊な表記のように見える#->もメソッドです。#@が「3 @ 4」などとすることでポイント(a Point)を作成したり、#/が「3 / 4」などで分数(a Fraction)を作成するのと同様に、レシーバを「キー(key)」に、パラメータを「値(value)」に据えた「関連(an Association)」というオブジェクトを作成します。case-switchでは、たまたま双方ともブロックが使われていますが、関連オブジェクト一般にはキー(レシーバ)と値(パラメータ)はどんなオブジェクトでも構いません。

 関連オブジェクトのキーと値は、それぞれkey、valueというメッセージを送信することで参照できます。

(#one -> 1) key     " => #one "
(#two -> 2) value   " => 2 "


 関連オブジェクトを単独で用いるようなことはあまりしませんが、辞書オブジェクト(a Dictionary)におけるキーと値の関係のように、二つのオブジェクトを関連付けるときに内部的によく利用されるので、名前や扱いは覚えておくとよいでしょう。

 #caseOf:あるいは#caseOf:otherwise:は、キーと値それぞれにブロックを束縛した関連オブジェクトを要素に持つ配列をある種の辞書代わりに用い、キーを評価した結果がレシーバと一致した場合に、対応する値ブロックの評価を行なう…という仕組みでcase-switch文をシミュレートしています。#caseOf:otherwise:では、第二パラメータにはいずれのケースにも当てはまらない場合の処理をブロックとして与えます。

 このcase-switch表現を用いたスクリプトを次に示します。血液型を尋ねるポップアップが現われるので、項目を選択して答えると、おおざっぱな性格判断の結果を返してきます。

| menu bloodGroup |
menu _ SelectionMenu fromArray:
   #(('A'  #a)
     ('B'  #b)
     ('AB' #ab)
     ('O'  #o)).
bloodGroup _ menu startUpWithCaption: 'blood type:'.
PopUpMenu confirm:
   (bloodGroup caseOf:
      {[#a]  -> ['nervous!'].
       [#b]  -> ['selfish!'].
       [#ab] -> ['volatile!'].
       [#o]  -> ['optimistic!']} otherwise: ['fishy!'])

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


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

This page has been visited 977 times.