何某日和

「カメラ」のち「ハンダゴテ」ところにより「プログラミング」 ── そんな私“かめきち”のウェブサイト

2012.09.26:Smalltalk文法

概要

 初回のペアプログラミングでは、Smalltalkの基礎基本となる部分を指南することになります。一方的な指南を避け、「見て・聞いて・考えて・手を動かして」というペアプログラミングを実践します。 とはいえ、取り組むことも多くまた冗長なため、退屈に感じるやもしれません。お菓子でもつまみながら楽しくSmalltalkに慣れてゆきましょう。

マシンメンテナンス

 ペアプログラミングの冒頭は、マシンメンテナンスに始まります。錆びた包丁や消毒しない包丁を使う料理人は、料理人として失格ですよね。 ソフトウェア技術者も同じで、メンテナンスをしていないマシンでソフトウェアを開発するなど許されることではありません。 「Smalltalk文法」という題にはあまり関係がないですが、まず環境整備をしっかりとしておきましょう。

 このページ(日々のマシンメンテナンス)に、メンテナンス方法についてまとめているので、 また必要なときに参照してもらえればと思います。今回は、Macintoshのみのメンテナンスを行います。

  1. 起動時に「コマンド+オプション+p+r」を押し、起動音が3回以上鳴るまで繰り返す。
  2. 「コマンド+s」を押しながら起動して「/sbin/fsck -fy」を実行する。「...to be OK」が出るまで繰り返した後に「shutdown -r now」
  3. ディスクユーティリティでアクセス権の修復を行う。
  4. ソフトウェア・アップデートを行う。
  5. 各自がインストールした各種ソフトウェア(アプリケーション)の更新を行う。

メンテナンスには時間がかかるやもしれません。お菓子などをつまみつつのんびりしましょう。

トランスクリプトに「Hello World」

 さて、メンテナンスが終わったら、早速Smalltalkを立ち上げましょう。そしてワークスペースを開けてください。

ワークスペースを立ち上げる ワークスペース

では、トランスクリプト上に「Hello World」という文字列を表示させてください。 既に青木塾などで「トランスクリプトへの文字列出力」を行なっていると思います。それを思い出して実装してみてください。 できたでしょうか?

このフレーズは、何も見ずにスラスラと書けるようになりたいプログラムの一つです。 これからSmalltalkプログラミングを進めてゆくにあたって、トランスクリプトへの出力は必須テクニックです。必ず覚えてください。

Transcript cr; show: 'Hello World'
Do it(実行する) トランスクリプトに文字列が表示される

オブジェクトをつかまえよう

 トランスクリプトに文字列を出力するプログラムは、Do it(実行)すれば画面に変化が現れるので良いのですが...。
次のプログラムをワークスペースに入力してください。

3 + 4

単純な足し算にしか見えないと思いますが、これも立派なSmalltalkプログラムです。これをDo itしてみましょう。 すると、何も起こらないので少し不安になるかもしれませんが、何も起こらないのが正常です。 プログラムはちゃんと動いていて、計算も行われるのですが、その結果が画面上に現れないだけなのです。

では、Do itではなく、Print it(表示)してみましょう。計算結果が表示されるはずです。

Print it(表示する) 計算結果が表示された

次に、Inspect it(検査)してみます。すると、新たにウィンドウ(インスペクタ)が開くはずです。

Inspect it(検査する) オブジェクトをつかまえた

インスペクト(検査)するということは、そのオブジェクトをひっつかまえるということです。つまり「3 + 4」の実行結果のオブジェクトをつかまえたのです。 今、画面上に開いた新しいウィンドウは、つかまえたオブジェクトそのものを表しています。(ここでは「7」というオブジェクトをつかまえた。) 何らかの処理の結果として得られたオブジェクトが、どのようなオブジェクトなのかを調べるとき、この「Inspect it」が役に立ちます。

タイトルバーに「a SmallInteger (immutable)」とありますが、これはSmallIntegerクラスのオブジェクトであるということを表しています。 試しに、ワークスペースに「3.14」と入力してインスペクトしてみてください。

Floatのオブジェクト(3.14)をインスペクト

Float(浮動小数点数)のオブジェクトになりましたね。

同じように「#a」「'hoge'」「true」「#(9 7 5 3 1)」「nil」もインスペクトしてみると...

Characterのオブジェクト($a)をインスペクト ByteStringのオブジェクト('hoge')をインスペクト Trueのオブジェクト(true)をインスペクト Arrayのオブジェクト(#(9 7 5 3 1))をインスペクト UndefinedObjectのオブジェクト(nil)をインスペクト

それぞれ左から、Character、ByteString、True、Array、UndefinedObjectのオブジェクトが得られたはずです。

このようにオブジェクトをつかまえられるのがSmalltalkの強みと言っても良いでしょう。 オブジェクトをひっつかまえて、質問したり、仕事を依頼したり。その「オブジェクトとの対話」のイメージを是非掴んでほしいのです。 次節では、オブジェクトに対して質問や仕事の依頼を投げますので、その「対話」を実感してもらえればと思います。

 最後に、Debug itを試して本節を終わりにします。

Debug it(デバッグする) デバッガ

デバッグすると、プログラム実行のステップをひとつずつ追っていくことができます。プログラムの流れを把握することも、またある時点での変数の内容を把握することも叶います。 デバッガのない環境でプログラミングする時の、あの真っ暗で漠然としたデバッグ作業など必要ありません。Smalltalkでは標準でデバッガを用意してくれていますので、大いに活用しましょう。

 デバッガのウィンドウには様々なボタンが用意されています。 これらを押すことでプログラムを1ステップ進めたり、奥に入り込んだり、少し戻ったりできますので、機会があれば弄ってみてください。

【コラム】どこまでSmallInteger?

 SmallInteger(小さな整数)と表現するくらいですから、きっとLargeIntegerというような整数もあるのでは?と思いますよね。 そこで「536870911」と「536870912」をインスペクトしてみてください。実はここが境目になっていて、片やSmallInteger、片やLargePositiveInteger(大きな正の整数)となります。

SmallIntegerのオブジェクト(536870911)をインスペクト LargePositiveIntegerのオブジェクト(536870912)をインスペクト

ここで登場した「536870912」という数値は、実は「2の29乗」を意味しています。

Spotlightで「2の29乗」を計算した様子

 Smalltalkのオブジェクトは32bitで表現されていて、ビット列最後尾の2bit(タグ)を除く先頭30bitで、オブジェクトの場所を知るための数値(Index of OTE (Object Table Entry))を表現します。 つまり、オブジェクトへの参照への参照(ポインタへのポインタ)ですね。

 ところが、プログラム中に頻出する整数(小さな整数:SmallInteger)のためにオブジェクトを生成して参照を作って...としていると、処理が重くなってしまうため、 整数の場合は特別に、オブジェクトへの参照(Index of OTE)に使っていた30bitの領域を用いて整数データを表現しています。 その30bitのうち1bitは符号ビットとして、残り29bitで数値の大きさを表現しますから、SmallIntegerは「2の29乗」まで表現できるのです。

【コラム】全てがオブジェクト

「Smalltalkでは、あらゆるものがオブジェクトとして扱われます」と教わったものですが、本当にそうなのか気になったので、コメントや空白をインスペクトしてみました。 本当にオブジェクトならインスペクタが開くはずです。

コメントをインスペクト スペースをインスペクト

おぉ...見事にインスペクタが開きました。どうやら、nil(UndefinedObjectのオブジェクト)になるようです。本当に全てはオブジェクトになるようですね。。。

ちなみに、本日のペアプログラミング「Smalltalk文法」向けに師から提供されていた資料には「オブジェクトが決まらない場合、nil となります。」とあります。 Smalltalkの作者も、うまい具合に逃げ道を作ったものですね(笑)

クラスと変数とメッセージ

 この節では、オブジェクトを生み、オブジェクトに命名し、オブジェクトと対話しますので、オブジェクトの実在感を垣間見てもらえればと思います。

オブジェクトを生む ── クラス

 まずオブジェクトを生んでみます。例えば、OrderedCollectionというクラスのオブジェクトを生むとすれば、

OrderedCollection new

これだけで良いのです。これを実行すればオブジェクトが生まれます。さあ、実行してみてください。

どうでしょう、実行しても何も起こらないはずです。少し驚くかもしれません。実は今、オブジェクトが生まれたのですが、すぐに死んでしまったのです。

オブジェクトは生まれた後に、命名されないと死んでしまいます。誰からも呼ばれないオブジェクトは、GC(ガベージコレクション)によって開放されてしまうのです。 次は、オブジェクトを死なせないためにも、生んだ後に命名してあげましょう。

オブジェクトに命名する ── 変数

 「命名する」というのは、そのオブジェクトとその名称を関連付けることです。これは変数への束縛(代入)によって行います。

| john |
john := OrderedCollection new

これでオブジェクトに「john」と命名することができました。この「john」という名前がプログラム中(スコープ中)に存在する限り、オブジェクト(john:ジョン)は生き続けることになります。 ただし、もう少し似合った名前にすることを勧めます。「aCollection」とか。

| aCollection |
aCollection := OrderedCollection new

良い感じですね。「とある集合」と名付けたわけです。クラス名に冠詞の「a」「an」「the」などを付すことがあります。覚えておくと良いでしょう。

さて、せっかく生まれたオブジェクトです、次はオブジェクトと対話してみましょう。

オブジェクトと対話する ── メッセージ

 「対話する」とは、メッセージを投げかけることに相当します。そこで、オブジェクトに「inspect」というメッセージを投げてみましょう。「aCollectionさん、あなたをインスペクトさせてください」という意味です。 このプログラムをDo itしてみてください。

| aCollection |
aCollection := OrderedCollection new.
aCollection inspect
プログラムを実行(Do it) 「とある集合」をインスペクトできた

実行してみると、aCollectionのインスペクタが開くと思います。aCollectionさん投げた「インスペクトさせてください」という依頼が通ったようです。

もうちょっと対話してみましょう。aCollectionさんは集合(Collection)なので、何らかのオブジェクトを追加することができます。aCollectionさんとの対話を通して、幾つかのオブジェクトを追加しましょう。 インスペクタの下半分にある入力欄に以下のプログラムを書いて、Do itします。

self add: 'hoge'
「hoge」を追加する 「hoge」を追加した

すると、nilだった1番目の要素に「hoge」が入りました。 ちなみに、インスペクタのウィンドウはaCollectionそのものを表しているので、プログラム中の「self」は、aCollectionのことを指します。 「自分(aCollection)に「hoge」を追加してください。」というプログラムだったのです。
では、これと同じように、2番目の要素として「foo」を入れてみましょう。

self add: 'foo'
「foo」を追加する 「foo」を追加した

これでオブジェクトとの対話方法の多少を会得したと思います。Smalltalkプログラミングは、オブジェクトとの対話で成り立ちます。本節で対話の感覚が掴めれば言うことなしです。

【コラム】メッセージの優先順位

メッセージには3つの種類があり、それぞれには異なった優先順位があります。今は必要にならないかもしれませんが、いずれ少し凝ったプログラムを書くときに、優先順位を意識せざるを得なくなります。 心の片隅にでも記憶しておいてください。まず、3種類のメッセージを見てみましょう。

aCollection inspect

これは「単項メッセージ」です。aCollectionという名のオブジェクトに対して「inspect」というメッセージを送っています。一番優先順位の高いメッセージが、この単行メッセージです。

3 + 4

これは「二項メッセージ」です。単なる計算式に見えますが、これは「3」というオブジェクトに対して「+」というメッセージを引数「4」と共に送っているのです。 単項メッセージの次に優先順位が高いメッセージになります。

aCollection add: 'hoge'
aCollection at: 1 put: 'bar'

この2つの例は「キーワードメッセージ」です。上は、オブジェクトに対して「add:」というメッセージを引数「hoge」と共に送っています。 下は、オブジェクトに対して「at:put:」というメッセージを引数「1」「bar」と共に送っています。この下の例は「分かち書き」というもので、JavaやCには見られない書き方です。 このキーワードメッセージは、優先順位の最も低いメッセージです。

1番目の要素として「bar」を入れる 1番目の要素として「bar」が入った

 最後に確認問題です。資料からかっぱらってきた例題ですが、次のプログラムはどのような順番で評価されるでしょうか。一度考えてみてください。

Rectangle origin: 1 asPoint + 2 asPoint extent: 3 asPoint + 4 asPoint

【コラム】「この仕事できますか?」

 ここまで「オブジェクトと対話することができる」と述べてきましたが、それを実感しやすい例をもう一つご紹介します。 あるオブジェクトがあるメッセージに対応できるか否かを質問することができるのです。

 OrderedCollection(順序集合)のオブジェクトに対して、メッセージ「add:」に対応できるか、またメッセージ「sleep」に対応できるかを質問してみます。

self respondsTo: #add:
メッセージ「add:」に対応できるか否かを質問する 「add:」は、できるらしい
self respondsTo: #sleep
メッセージ「sleep」に対応できるか否かを質問する 「sleep」は、できないらしい

要素の追加(add:)はできるけど、寝ること(sleep)はできないようですね!

小技いろいろ

コメント

 CやJavaでは「//」で始めるコメントですが、Smalltalkでは「"」を用いて次のように書きます。

"ふふふ、ここがコメントなのですよ。"

代入

 CやJavaでは「=」で行う代入ですが、Smalltalkでは「:=」(ゲッツ)を用いて次のように書きます。

aCollection := OrderedCollection new

カスケード式

Transcript cr.
Transcript show: 'Hello'.
Transcript space.
Transcript show: 'World'

 同じオブジェクトに何度もメッセージを投げる...って面倒ですよね。カスケード式という書き方をすれば、この冗長な書き方をシンプルにまとめることができます。

Transcript
    cr;
    show: 'Hello';
    space;
    show: 'World'

トランスクリプト

トランスクリプト

 既に何度も使っていますが、このウィンドウがトランスクリプトです。プログラム上では「Transcript」というオブジェクトとして使います。 プログラムのデバッグの際に重宝しますので、よく覚えておきましょう。

Transcript cr (キャリッジリターン(改行)を挿入する)

Transcript space (スペースを挿入する)

Transcript tab (タブを挿入する)

Transcript show: 'hogehoge' (hogehoge(文字列)を挿入する)

Transcript show: anObject printString (オブジェクトを文字列表現にして表示する)

間隔

#(1 2 3 4 5 6 7 8 9 10 11 12 ... 99 100)

こんな配列を作るのは面倒なので、次のような書き方をします。

(1 to: 100) asArray
Print it(表示する) 間隔(Interval)

また、刻み幅を調節することもできますので、一度試してみてください。

(0 to: 100 by: 10) asArray

ブロッククロージャ

 次の例をDo itしてみてください。トランスクリプトに文字列が出力されるはずです。

[Transcript cr; show: 'Hello World'] value
トランスクリプトに文字列を出力するブロッククロージャ トランスクリプトに出力された「Hello World」

 では、次の例も実行してみてください。

| aBlock |
aBlock := [Transcript cr; show: 'Hello World'].
aBlock value

きっと同じ結果が得られるはずです。さて、次の例も。

| aBlock |
aBlock := [:aString | Transcript cr; show: aString].
aBlock value: 'Hello World'

[ ]で囲んでいる部分が「ブロッククロージャ」です。 ブロッククロージャに対して、valueメッセージを送ると、[ ]内のプログラムが実行されます。 つまり、プログラムだけ書いておいて、実行を遅延させるための機構なのです。

 このブロッククロージャは、Smalltalkプログラムを美しく書く際に用いたり、高度なプログラムを書く際に必要となりますが、それはまた追々説明するとして、 今回は、次節以降の「分岐」「繰り返し」に用いるブロッククロージャを習得しましょう。

分岐

 CやJavaで「if文」というものがありますが、それに相当するものをご紹介しましょう。

| aBlock |
aBlock := [:aString | Transcript cr; show: aString].
JunSensorUtility altDown
    ifTrue: [aBlock value: 'Alt key is down.']
    ifFalse: [aBlock value: 'Alt key is not down.']

判定条件は「JunSensorUtility altDown」で、altキーを押下している場合はtrueが、押下していない場合はfalseが応答されます。 Do itする際に、altキーを押しているなら、トランスクリプトに「Alt key is down.」と表示されます。 altキーを押していないなら、トランスクリプトに「Alt key is not down.」と表示されます。

繰り返し

 分岐に同じく、繰り返しの仕組みも用意されています。

3 timesRepeat: [Transcript cr; show: 'Hello World']
timesRepeatメッセージで繰り返し
(1 to: 4) do: [:aNumber | Transcript cr; show: aNumber printString]
間隔(Interval)で繰り返し
[JunSensorUtility altDown] whileTrue: [Transcript show: 'a']
whileTrueで繰り返し

繰り返しにはいろいろな形があるということを会得できたでしょうか。

バブルソート

ここまでに習得した分岐・繰り返しを使えば、バブルソートプログラムを拵えることもできましょう。

| anArray aNumber aSize |
anArray := Array withAll: #(1 3 5 7 9 8 6 4 2).
aNumber := 1.
aSize := anArray size.
[aNumber < aSize] whileTrue: 
    [(anArray at: aNumber) <= (anArray at: aNumber + 1)
        ifTrue: [aNumber := aNumber + 1]
        ifFalse: 
            [anArray swap: aNumber with: aNumber + 1.
            aNumber := 1]].
^anArray

さいごに

 今日はSmalltalkにたくさん触れましたが、冒頭に述べたことを覚えているでしょうか。「トランスクリプトへの文字列出力」のプログラムをスラスラ書けるようになること、と言いました。 ここでもう一度ワークスペースを開き、トランスクリプト上に「Hello World」と表示させてみてください。 このテクニックを覚えたらば今週の目標は達成としましょう。お疲れ様でした!