演算子の優先順位についてです。以下の問いを見てください。
問い 1
2 + 3 * 4 はいくらか?
答え 1
14
2 + 3 * 4 を計算するとき、私たちは具体的には次のような順序で計算をしています。
このことから、次のことが言えます。
すなわち、こうです。
Delphi で使用できる演算子(たとえば、算術演算子、論理演算子、文字列演算子など)には優先順位がついています。たとえば例で見たように、2 + 3 * 4 では、+ よりも * の方が優先順位が高いですね。優先順位が高いとは、オペランド(被演算子)が「どっちの方(演算子)へ先に引き寄せられるか」というような解釈をすると分かりやすいかもしれません。2 + 3 * 4 を例にすると、次のようになります。
2 + 3 * 4 ↓ + と * では * の方が優先順位が高い。つまり、"3" は * の方へ引き寄せられる。 2 + 12 ↓ 14
「引き寄せられる」と表現しましたが、より正確には、優先順位の高い演算子は優先順位の低い演算子よりも先に評価されるということになります。「評価」というのは、ここでは「計算」と読み替えても構いません。
それでは、問い 2 を見てください。
問い 2
2 + 3 * 4 = 20 が成り立つには、どうすれば良いか?
答え 2
(2 + 3) * 4 = 20
演算子には優先順位がついており、その優先順位を変更することはできません。+ 演算子よりも * 演算子の方が優先順位は高いです。この規則は変更できません。優先順位を変更するには、上記の「答え 2」のようにカッコを使用します。カッコ内の式は先に評価され、1 つのオペランドとして扱われます。
ここまでをサンプルプログラムで確認してみましょう。
procedure TForm1.Button1Click(Sender: TObject); var Sum: Integer; begin Sum := 2 + 3 * 4; ShowMessage(IntToStr(Sum)); // 14 Sum := (2 + 3) * 4; ShowMessage(IntToStr(Sum)); // 20 end;
次のプログラムで、変数 Sum1, Sum2, Sum3 の値はいくらですか
procedure TForm1.Button1Click(Sender: TObject); var Sum1, Sum2, Sum3: Integer; begin Sum1 := 1 * 2 + 3 * 4; ShowMessage(IntToStr(Sum1)); Sum2 := 1 * (2 + 3) * 4; ShowMessage(IntToStr(Sum2)); Sum3 := 1 * 3 + (2 * (3 + 4)); ShowMessage(IntToStr(Sum3)); end;
ここまでで、+, * 演算子しか取り上げていませんでした。この節では、もう少し詳しく演算子の優先順位について取り上げていきます。
まず、演算子の優先順位についての表をまとめておきます(ヘルプより抜粋)。
演算子 | 優先順位 |
---|---|
@, not | 1 位(最高) |
*, /, div, mod, and, shl, shr, as | 2 位 |
+, -, or, xor | 3 位 |
=, <>, <, >, <=, >=, in, is | 4 位(最低) |
この表からも + 演算子と * 演算子では * 演算子の方が優先順位が高いことが分かりますね。
優先順位の一覧表を見ますと、+ 演算子と - 演算子の優先順位は等しいということが読み取れます。では、例えば次のような式では、どのような評価順になるのでしょうか。
1 + 2 * 3 - 4
まず、一番優先順位の高い * 演算子が最初に評価されます。2 * 3 が 6 となりますので、以下のような式となります。
1 + 6 - 4
このとき、
が気になります。この場合では、どちらが先に評価されても計算結果は同じですが、+ 演算子と - 演算子のように、優先順位が等しい場合には、左側の演算が先に実行されます。
つまり、
となります。以上より、
1 + 6 - 4
の場合には、1 に 6 を足して、その結果から 4 を引くことになります。
複数の演算子を同時に使用する時(例えば、2 + 3 * 4 などのように、複数の演算子(+ と * 演算子)を使用する時)には、演算子の優先順位に注意する必要があります。ここでは or 演算子と not 演算子を取り上げて、その例を見ていきます。
or 演算子は、論理演算子と呼ばれます。オペランドに論理型を取るとき、その論理和を Boolean で返します。(オペランドが整数型の場合には、ビットごとの論理和を返します)。
or 演算子のサンプルとして、「 x の値が -1 か又は 1 であるなら、メッセージを表示する」というプログラムを書いてみましょう。
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin x := -1; if x = -1 or x = 1 then // まちがい! ShowMessage('x = -1 または x = 1'); end;
コンパイルすると、「互換性のない型です」とコンパイルエラーが出ます。このサンプルで注目すべき箇所は、以下です。
if x = -1 or x = 1 then
ここでは、= 演算子と or 演算子を使用しています。優先順位の一覧表によると、= と or では、or の方が優先順位が高いです。つまり上記の式は、次のように解釈されていることになります。
if (x = (-1 or x)) = 1 then
これでは、x が -1 か又は 1 かどうかを調べることにはなっていませんね。正しくは、次のようにカッコを使用する必要があります。
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin x := -1; if (x = -1) or (x = 1) then // x が -1 か又は 1 かどうか。これは正しい ShowMessage('x = -1 または x = 1'); end;
not 演算子は、オペランドが整数型であれば、ビットごとの否定を返し、オペランドが論理型であれば、その否定を返します。
サンプルコードが若干まわりくどいですが、「 整数型の変数 x の値が 1 でないかどうか」を not 演算子を使用して書いてみます。
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin x := -1; if not x = 1 then ShowMessage('x は 1 ではない'); end;
このサンプルで重要なのは、以下です。
if not x = 1 then
優先順位の一覧表を参照すると、not 演算子と = 演算子では、not 演算子の方が優先順位が高いです。ですから、これは次のように解釈されます。
if (not x) = 1 then
このコードは、 x のビットごとの否定と 1 が等しいかどうか、すなわち 0 と 1 が等しいかを見ていることになります。
つまり、
if 0 = 1 then
という意味になっており、プログラマが意図すること( x の値が 1 でないかどうかを調べること)とは全く別のものになってしまっています。
これは、次のようにカッコを使用します。
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin x := -1; if not (x = 1) then // カッコを使用する. ShowMessage('x は 1 ではない'); end;
演算子の優先順位について見てきました。大事なことは、演算子には優先順位があり、それに従って評価される順序が決まっているということです。