このセッションでは メッセージの合成を学びます。
優先順位がどのように働くか 階層を見ます。
まず問題ですが
「単項メッセージのシーケンスがある場合には 何が起こるでしょうか？」
次のような表現式があるとします。
1000 factorial class name 何が起こるでしょう？
これらの括弧があるのと同じように 実行されます。
見ての通り煩雑ですね。
同じレベルのメッセージが複数ある場合
(単項、二項またはキーワードの いずれでも同じです）
左から右に評価されていきます。
まず 1000 に factorial メッセージ(階乗) を送ります。
そして factorial の結果に対して class メッセージを送ります。
そしてこの新しいオブジェクトに
name メッセージが投げられます。
LargePositiveInteger が返ってきます。
1000の階乗(factorial)は大きな数です。
10000の階乗を試すこともできます。
するとさらにもう少し「長い」数になります。
括弧つきのメッセージもあります。
先に実行される必要がある時です。
次に単項メッセージ、次に二項メッセージ その次にキーワードメッセージです。
同じレベルの場合には 左から右へ行きます。
1つ例題をやってみましょう。
単項メッセージ squared と
二項メッセージ + があります。
単項メッセージが勝ちます。
まず squared を送信して
次に + メッセージを送信します。
11が返ってきます。正解です。
別の状況があります。
raisedTo: メッセージで累乗を求めます。
二項メッセージとキーワードがあります。 二項メッセージが勝ちます。
まず 5 が得られ
そして raisedTo: が実行され 32が返ってきます。 正解です。
これはもう少し複雑な例です。
Pharo では色はオブジェクトです。
Color クラスがあります。
3つの単項メッセージがあります。 gray と white と black です。
2つの二項メッセージがあります。 = と - です。
では何が起こるでしょう？ システムはまず各単項メッセージを実行します。
それぞれ灰色、白、黒の オブジェクトを返します。
そしてメッセージを送ります。
- メッセージと = メッセージから 選ばなければなりません。
灰色オブジェクトに - メッセージを 白を引数として送ります。
黒が返ってきます。
2つの色を比較します。 黒 = 黒になります。trueです。
この例題は
Pharoで自動変換ができるというもので
ある種の冗談です。
この表現式です。 1 class maxVal + 1
2つの単項メッセージがあります。 class と maxVal です。
そして二項メッセージが1つです。
この表現式はどう実行されるでしょうか？
小さな整数 1 に class メッセージを送ります。
SmallInteger が返ってきます。 1 は整数ですから。正しいです。
1 class の結果である SmallInteger に maxVal メッセージを送ります。
maxVal メッセージとは何でしょう？
「あなた(SmallInteger)が符号化できる 最大の数は何ですか？」という意味です。
この大きな数が得られます。 この、最後が23で終わる数です。
この数に + 1 メッセージを送ります。
返ってくるのは小さな整数(SmallInteger) ではありません。
なぜなら、SmallIntegerで表現できる 最大の数がこれだからです。
結果として 答えはSmallIntegerにはできません。
この数のクラスが何かを知りたいです。
括弧で囲んで、class を送ります。 LargePositiveInteger が返ってきます。
これが最大のSmallIntegerで
これが最小のLargePositiveIntegerです。
括弧で囲むのは
それがないと class は 1 に送られてしまうからです。
それでは困ります。 結果に対して class を送りたいのです。
矩形を生成したとします。
その矩形の右下の点が欲しい時に
こう書くと 実行するとクラッシュします。
システムは理解できません。 100 は bottomRight を理解できません。
なぜ？ bottomRightは単項メッセージなので
他のメッセージよりも先に実行されます。 bottomRightはレシーバーである100に送られます。
100 はこの API を理解できません。 なぜなら 100 は Rectangle(矩形)ではないからです。
この表現式のように 括弧で囲まなければなりません。
どのように動くのでしょう？ 括弧で囲まれたものが先に実行されます。
内側では2つの二項メッセージがあり それらが実行されます。
2つの点が生成されます。
そして extent: メッセージを送ります。 矩形を作る一方の点に送ります。
点 0@0 があります。
extent:メッセージに広がり 100@100 を渡します。
この矩形が返ってきます。
それに bottomRight の値を尋ねます。
矩形の
ここの点の値を返してきます。
100@100 です。
Pharo では色々な事がシンプルだと これまで言ってきました。
メッセージしかありません。
+ はメッセージです。他と同様。 優先順位はありません。
+ をDSL(ドメイン特化言語)に使えるのは 良いことです。
+ を数学的なオブジェクトとは関係ない オブジェクト間に使うことができます。
Java ではこれはできません。 C++ では演算子を再定義することで可能です。
Pharo では
+ は他のメッセージと同じ扱いにすると 決めることで解決しています。
強調すべきはシンプルさです。
代償があります。 数学的な結合順位がありません。
例を見てみましょう。
この表現式では 2つの演算子があります。
2つの二項メッセージです。
左から右に実行されます。
5 が得られ、50が返ってきます。 学校で習ったのとは違います。
これを解決するためには 括弧で囲む必要があります。
* を括弧で囲みます。
Pharo で算術演算子を使う時には 注意が必要です。
数学的演算子はメッセージにすぎないからです。
もう1つの例です。
1/3 + 2/3 と書くと 正しい答えが得られません。
システムがこの式を先に実行するからです。
左から右へ実行されるからです。
括弧で囲むと 正しい結果が得られます。
興味深い点を1つ挙げると
(1/3) + (2/3) と書くと 整数の 1 が得られます。
1.000... や 0.999... ではありません。
正確な分数の操作を行います。 正確な計算結果が得られます。
まとめると
メッセージには3種類あります。 もう知っている筈です。
単項、二項、そしてキーワードです。
まず最初に括弧が実行されます。
そして単項、二項、そしてキーワードです。
同じレベルの場合には
例えば2つの単項メッセージがある場合には 左から右に進みます。
数学的な結合順位はありません。
数学的な演算子も単なるメッセージです。
多くの言語と異なるのは
引数がメッセージの構造の中に置かれることです。 例えば between:and: です。