第1章 メッセージ
キーワード型のメッセージ
まずはおなじみのHello world!から始めましょう。以下のようなファイルをテ キストエディタで作成し、例えば hello.st というファイル名で 保存して実行(FileIn)してください。
1 Transcript showCR: 'Hello Smalltalk world!!'.
2 !
画面には
Hello Smalltalk world!
と表示されたはずです。Smalltalkの世界へようこそ! まずは、このプログラム が何をしているか説明しましょう。まず、'Hello Smalltalk World!!' のよう に ' (シングルクォート)で囲まれた部分が文字列です。C++等 のダブルクォートと違って、Smalltalkではシングルクォートを使うことに注意 してください。ちなみに、ではダブルクォートは何に使うのでしょう? 実は、 Smalltalkではコメントを " (ダブルクォート)で囲むので す。ですから、Smalltalkのコメントは例えば、
1 "a program to say hello"
2 Transcript showCR: 'Hello Smalltalk world!!'.
3 !
のように使われます。Smalltalkの文字列は、' で囲まれた文字列定数も含め、全てStringクラスのオブジェクトです。
続いて、Transcript ですが、これは例の Launcher ウィンドウの下側にあるメッセージ表示領域(Transcript)を表すオブ ジェクトです。本稿では標準出力の代わりに使用します。showCR: は Transcript のメソッド(C++では「メンバ関数」と呼ばれますね)で、引数として与えられたオブジェクトを表示し、改行 します。つまり、このプログラムは、C++風の疑似コードで書くと、
#!smalltalk
Transcript.showCR:("Hello Smalltalk world!");
に相当しているのです。: (コロン)もメソッド名の一部であることに注 意して下さい。このように引数を一つとるようなメソッド名は通常 : で終わり ます。(例外は後で説明します) 一般に、C++等でオブジェクト obj のメソッ ド method を、param を引数にて実行するには、
obj.method(param)
のように書きますが、Smalltalkでは . (ドット)も要りませんし、( ) も要りま せん。ただ、オブジェクトの後に、スペースを挟んで( : で終わる)メソッドの 名前を書き、続いて引数を書けば良いのです。つまり、
1 obj method: param
のような感じですね。続く . (ドット)は式の終端を表します。C++等で の ; (セミコロン)と同じです。
今まで、「メソッドを実行」と言ってきましたが、これはSmalltalkではあまり 良い言い方ではないようですありません。Smalltalkでは、 メッセージを送ると言います。 と りあえずは、他の言語で言うところの「メソッドを実行」と同じ意味だと思っ て問題ないと思います。また、メッセージを受け取るオブジェクト(ここでは Transcript)のことを、 レシーバ(receiver)と呼びま す。つまり、このプログラムは日本語で、「レシーバ Transcript に『showCR: 'Hello Smalltalk world!'』というメッセージを送る」と言い替えることが出来 るわけです。
さて、では最後の行に書いてある ! は何でしょ う? 実はこの ! は、Smalltalkの文法で本質的なものではなく、 fileOut 形式と呼ばれる形式でソースコードを書いて いるために必要なものです。fileOut 形式のソースコードは多くのメソッドの 定義などを並べて書くものなので、ソースコードの切れ目がどこにあるのか、 Smalltalkのパーサに分かりやすいようにしなければなりません。(C++等では { } でメソッドの定義などを区切りますよね) ですがSmalltalkのプログラムは本 来、統合開発環境上でメソッド毎に書くものなので、言語の文法自体に は、このようなソースコードの切れ目を表す記法は用意されていません。仕方が ないので、fileOut 形式では、便宜上 ! でソースコード を区切ることにしています。! で区切られた、ソースコードの1単 位をチャンク(chunk)と呼びます。例え1行のプログラムであっても、チャ ンクの終端を示す ! は書かねばなりません。では、ここで意図的 に気付かないフリ(;)をしていたところにもどりましょう。文字列中の !! です。プログラム内の文字列では !! のように ! を2つ続けて書いていますが、画面に表示されているのは1つの ! だけです。これも fileOut 形式の便宜上の仕様で、チャンク終 端以外の ! は、チャンク終端と間違われないように、 全て !! のようにエスケープしなければならないのです。 これは本当に全ての ! に関して当てはまります。コメント内 の ! ですら、エスケープしなければなりません。反 対に、 もし外部エディタを使わず、統合開発環境内のエディタで直接プログラ ムを組 んでいる場合は、チャンク終端の ! は必要ありませんし、文字列 中の ! も !! にする必要はありません。
他にもいろいろなものを表示してみましょう。まずは整数(Integerクラ ス)と浮動小数(Floatクラス)のリテラル(つまり、定数)です。
1 Transcript showCR: 1234567.
2 Transcript showCR: 3.14159265.
3 !
今度は、べき乗といきましょう。2.4の3.5乗です。
1 Transcript showCR: (2.4 raisedTo: 3.5).
2 !
ここで使用しているのはべき乗を返すraisedTo: メソッド (ArithmeticValue クラスで定義)です。raisedTo: メソッドの 実行全体が ( ) で囲まれているのは何故でしょう? これは下で説明する、2つ以 上の引数をとるメソッドと区別できるように、処理の優先順位を決めるために必 要なものです。
では、引数を2つ以上取るメソッドを使ってみましょう。
1 Transcript showCR: ('hello' copyFrom: 2 to: 4).
2 !
を実行すると、文字列 'hello' の2文字目から4文字目まで(つまり、ell )が表 示されたと思います。(注:Smalltalkの配列(文字列含む)の添 字は1始まりです) このプログラムではレシーバ 'hello' の、 copyFrom:to: メソッドを実行しています。引数は 2 と 4 です。この書 き方がC++(等)プログラマを混乱させるようですが、Smalltalkの
1 obj copyFrom: arg1 to: arg2
はC++等の
obj.copyFrom_to(arg1, arg2)
と同じ意味です。つまり、 copyFrom:to: 全体で一つのメソッドの名前なのです。Smalltalkでは、各引数の意味を明確にするた めに、引数をこのようにメソッドの名前の間に挟みます。 (Smalltalkerな方への言い訳:メッセージだとかセレクタだとかメソッ ドだとかというタームは、ある程度意図的に無節操に使用しています) この調子でどんなに 引数が多いメッセージにも対応できます。例えば、
1 Transcript showCR: ('hello Smalltalk' replaceAll: $l with: $L from: 4 to: 10).
2 !
は、文字列に replaceAll:with:from:to: メッセージを送っています。 ここでは文字列の4文字目から10文字目の間に含まれる l を全て L に置き換え たものを返させています。文字を表すには L 等と書きます。これはC++等の L' に相当します。
ちなみに、この形式の、引数をとるメッセージを、後述の「単項型」と区別して キーワード型のメッセージと呼びます。
単項型のメッセージ
Smalltalkの整数は任意の桁数を格納できます。これを確かめるために、 (どこでも見る例ですが) 200 の階乗を表示させてみましょう。
1 Transcript showCR: 200 factorial.
2 !
ちゃんと、えらい桁数の結果が表示されたはずです。このプログラム(の1行 目)は、書き 換えると
1 Transcript showCR: (200 factorial).
と等価です。つまり、200 factorial が先に実行され(レシーバ 200 (Integerクラス)に factorial メッセージが送られ)、続いてその返値 (200の階乗) をTranscriptに表示しています。 単項型のメッセージはキーワード型より先に実行される(優先順位が高い) ということです。
同じ単項型のメッセージの場合は、メソッドは左から順に実行されてゆ きます。つまり、
1 Transcript showCR: 2 squared factorial.
は、
1 Transcript showCR: ((2 squared) factorial).
と等価です。ちなみに、squared はレシーバ (ここでは 2 )の2乗を返す メソッドです。
ついでにいくつか数学関数を紹介しましょう。
1 Transcript showCR: 12 negated. "符号反転"
2 Transcript showCR: -20 abs. "絶対値"
3 Transcript showCR: 30 degreesToRadians. "度をラジアンに"
4 Transcript showCR: 0 sin. "三角関数"
5 Transcript showCR: 1.23 exp. "指数関数"
6 Transcript showCR: 12.5 ln. "自然対数"
7 Transcript showCR: 2 sqrt. "平方根"
8 Transcript showCR: 12345 squared. "2乗"
9 !
等々。より詳しくは、Numberクラス(各種数学関数を適用できる オブジェクトのクラス)やArithmeticValueクラス(四則 演算の可能なオブジェクトのクラス)のマニュアルを参照してください。 いくつかの数学定数はFloatクラス(等)のクラスメソッド(C++でいうと ころの「静的メンバ関数」)で提供されています。クラスメソッド の実行も通常のメソッドと同じで、
クラス名 メソッド名
とすればOKです。例えば、
1 Transcript showCR: Float infinity. "無限大"
2 Transcript showCR: Float e. "自然対数の底(ネイピア数)"
3 Transcript showCR: Float pi. "円周率"
4 Transcript showCR: Float unity. "乗法の単位元(つまり、1.0)"
5 Transcript showCR: Float zero. "加法の単位元(つまり、0.0)"
6 !
テスト用のメソッド達は以下のようなものがあります。
1 Transcript showCR: 3 even. "偶数かどうか"
2 Transcript showCR: -1 negative. "負かどうか"
3 !
これらは条件が真ならばtrueを、偽ならばfalseを返します。
カスケード
まず、今まで画面表示には Transcript の showCR: メソッドばかり使って来ま したが、他のメソッドも紹介しましょう。例えば、
1 Transcript show: '10 の階乗は '. "改行をしない"
2 Transcript showCR: 10 factorial. "改行をする"
3 !
や、
1 Transcript space. "空白を1文字出力"
2 Transcript spaces: 5. "空白を5文字出力"
3 Transcript tab. "タブを出力"
4 Transcript cr. "改行"
5 !
といったことが出来ます。
ところで、上のプログラムは最初が Transcript ... Transcript ばっかりで見苦しいですし、キーボードを打つのも疲れてきます。 このような時に使える糖衣文法としてカスケード(cascade)があります。 カスケードを使うと、上のプログラムは以下のように書き換えることが出来ます。
1 Transcript space;
2 spaces: 5;
3 tab;
4 cr.
5 !
3行目までが . (ドット)ではなく ; (セミコロン) で終わっていることに注意してください。こうすることで、同じオブジェクトに 複数のメッセージを連続して送ることが出来ます。例えば、1行目で space メッ セージを Transcript に送った後、;を書いていますが、これは 「直後のメッセージ(spaces: 5)も、直前のレシーバ(Transcript)に送れ」とい う意味です。
演算子型のメソッド
今まで引数があるメソッドと無いメソッドを紹介してきましたが、四則演算は登 場しませんでした。Smalltalkにも四則演算等を行うための演算子型のメソッ ドがあります。
1 Transcirpt showCR: 10 + 5.
2 !
画面には正しく 15 と表示されたことでしょう。10 + 5 というところでは、何も特別な構文が使われているわけではありませ ん。ただ、オブジェクト 10 (Integerクラス)が + メソッ ドを持っており、5 を引数として + メッセージを 10 に送っているだけなので す。演算子型のメソッド名は、引数を取るのに : (コロン)で終わっていません。 そのかわり、記号のみからなるメソッドは自動的に演算子型のメソッドと認識さ れます。一定の規則の元で、自由に新たな演算子を作ることが出来ます。 (Smalltalk/Xでは、特に予約されていない記号を除いて、好きな記号を3 文字まで組み合わせた演算子を作ることが出来るようです) 演算子型の メソッドは全て、引数を1つだけ取らなければなりません。もっと複雑な 演算を行うには、例えば
1 Transcirpt showCR: 10 * 5 + 3.
2 !
とします。これは「10 * 5 の返値に、3 を引数として + メッセージを送ってい る」わけです。演算子型のメソッドの優先順位は、 引数を取らないメソッドより低く、引数を取るメソッドより高い と決まっています。ですから、例えば、
1 Transcript showCR: 10 + 3 factorial.
は自動的に
1 Transcript showCR: (10 + (3 factorial)).
と解釈されます。Smalltalkの演算子型のメソッドを使う上での注意点は以下の プログラムを実行してみると分かります。
1 Transcript showCR: 1 + 2 * 3.
2 !
画面には何と表示されるでしょう? 「7」というのが多い答えでしょうが、残念 ながら間違いです。正解は「9」。Smalltalkの演算子はただのメソッドに過ぎな いということに注意してください。他のメソッドと同じく、演算子型のメソッド の間には優先順位がないのです。ですから、上のプログラムの1行目は、
1 Transcript showCR: (1 + 2) * 3.
と解釈されてしまいます。これを防ぐには ( ) を使って、
1 Transcript showCR: 1 + (2 * 3).
とします。
上では + (加算)と * (乗算)を使いましたが、Smalltalkで頻繁に使われる演算 子型のメソッドを他にもいくつか紹介しましょう。
1 Transcript showCR: 20 - 5; "減算"
2 showCR: 14 / 3; "除算"
3 showCR: 'hello ', 'world'; "文字列連結"
4 showCR: 10 = 10; "等しい"
5 showCR: 10 ~= 10; "等しくない"
6 showCR: 10 > 5. "大なり"
7 !
Smalltalkでは , (コンマ)は文字列を連結するための演算子なんですね。 このプログラムではカスケードを思いっきり使っています。 ここで注目して頂きたいのは除算です。画面には何と表示されたでしょうか? 多くのプログラミング言語のように (小数点以下切捨てで) 4 ではありませんし、また 4.666… でもありませんよね。(14/3) と表示されたはずです。実は、整数同士の除算 は分数を返すのです。これで、例えば 14 / 3 * 3 のような計算で厳密 な値を得ることが出来ます。(ちなみに、分数を表すのはFractionクラス です) 分数を小数(Floatクラス)に変換するには、asFloatメソッドを使っ て、
1 (14 / 3) asFloat
とします。また、割算の結果を整数で得る (小数点以下切捨て) には、// メソッ ドを、余りを求めるには \\ メソッドを使います。
複素数を扱うには Complex クラスを使用します。複素数の書き方にはい ろいろなものがあります。最も直感的なものは、i メソッドを使うこと でしょう。
1 Transcript showCR: 2.8 + 4.2 i.
2 !
とか。i は浮動小数点数 4.2 のメソッドであることに注意してください。 ( i は Integer クラスと LimitedPrecisionReal クラスで定義されて いるようです)
最後にワンライナーのサンプルプログラムを書きたいと思います。その前にユー ザからの入力を得る方法を紹介しましょう。CUIでSmalltalkを使う場合は、他の プログラミング言語と同じく、標準入力からユーザの入力を受け付ければ良いの ですが、Smalltalk/Xで完全にCUIのプログラムを作る方法を私が知らな いので、ここでは中途半端にGUIを使うことにしましょう。
1 Transcript showCR: (DialogBox request: 'Enter something:').
2 !
実行すると、Enter something: と上部に表示されたダイアログボックスが表示 され、下のテキストボックスにテキストを入力して OK ボタンをクリックすると、 入力された文字列が Transcript に表示されます。ここで使用しているのは DialogBoxクラスの request: クラスメソッドです。request: は 引数として与えられた文字列を表示したダイアログボックスを表示してユーザか らの入力を受け付け、入力された文字列を返します。ちなみに、少なくとも私の 環境では日本語は使えていません。
これを踏まえて、入力された小数の列をソートし、小さい順に並べたものを出力 するワンライナーを書きましょう!
1 Transcript showCR: (DialogBox request: 'Enter some floats:') asCollectionOfWords asFloatArray sort.
2 !
文字列 (String) クラスの asCollectionOfWords メソッドは文 字列をスペースで単語に区切ります。返値は配列で、要素は各単語を格納した文 字列です。asFloatArray メソッドはレシーバの各要素を小数に変換し た配列を返します。sort メソッドはレシーバの要素を小さい順にソート します。sort の返値はレシーバ自身、つまりソートされた配列自身なので、そ れを Transcript showCR: に渡して表示させているのです。
