式(Object Pascal)

提供: Appmethod Topics
移動先: 案内検索

基本的な構文要素:インデックス への移動

Object Pascal の式を形成する際の構文ルールについて説明します。

このトピックでは、以下の項目を取り上げます。

  • 有効な Object Pascal 式
  • 演算子
  • 関数呼び出し
  • 集合構成子
  • インデックス
  • 型キャスト

式とは、値を返す構文です。Object Pascal 式の例を次の表に挙げます。

×

変数

@X

変数 X のアドレス

15

整数定数

InterestRate

変数

Calc(X, Y)

関数呼び出し

X * Y

X と Y の積

Z / (1 - Z)

Z を (1 - Z) で割った商

X = 1.5

Boolean

C in Range1

Boolean

not Done

Boolean の否定

['a', 'b', 'c']

集合

Char(48)

値型キャスト


最も単純な式は、変数と定数です(「データ型、変数、定数」で説明しています)。複雑な式は、演算子、関数呼び出し、集合構成子、インデックス、型キャストを使って、より単純な式から組み立てられます。

演算子

演算子は、Object Pascal 言語に組み込まれた定義済みの関数と同様の動作をします。たとえば、(X + Y) という式は、X および Y という変数(オペランドと呼びます)と、+ 演算子で構成されています。X および Y が整数または実数を表している場合には、(X + Y) はその合計を返します。演算子には、@not^*/divmodandshlshras+-orxor=><<><=>=inis があります。

@not^ の演算子は、オペランドを 1 つ取る単項演算子です。他の演算子はすべて、オペランドを 2 つ取る二項演算子ですが、+- だけは例外で、単項演算子としても二項演算子としても機能します。単項演算子は、-B のようにオペランドの前に付くのが普通ですが、^ だけは例外で、P^ のようにオペランドの後に付きます。二項演算子は、A = 7 のように、オペランドとオペランドの間に置かれます。

演算子の中には、渡されたデータの型によって異なる動作をするものがあります。たとえば not は、オペランドが整数の場合にはビット否定を行い、オペランドが Boolean の場合には論理否定を行います。このような演算子については、以下の説明の複数の区分に含まれています。

^isin 以外の演算子はどれも、バリアント型のオペランドを取ることができます。詳細は、「バリアント型」を参照してください。

この後のセクションは、Object Pascal のデータ型に関するある程度の知識を前提としています。詳細は、「データ型、変数、定数」を参照してください。

複雑な式での演算子の優先順位については、このトピック後半の「演算子の優先順位」のセクションを参照してください。

算術演算子

算術演算子(実数または整数のオペランドを取ります)には、+-*/divmod があります。

二項算術演算子:

演算子 演算 オペランドの型 結果の型

+

加算

整数、実数

整数、実数

X + Y

-

減算

整数、実数

整数、実数

Result -1

*

乗算

整数、実数

整数、実数

P * InterestRate

/

実数除算

整数、実数

実数

X / 2

div

整数除算

整数

整数

Total div UnitSize

mod

剰余

整数

整数

Y mod 6


単項算術演算子:

演算子 演算 オペランドの型 結果の型

+

符号恒等

整数、実数

整数、実数

+7

-

符号反転

整数、実数

整数、実数

-X


算術演算子には以下のルールが適用されます。

  • x / y の値は、x および y の型に関係なく、Extended 型になります。算術演算子では、オペランドに 1 つでも実数型のものがあれば、結果は Extended 型になります。実数型のオペランドがなく、1 つでも Int64 型のものがあれば、結果は Int64 型になります。それ以外の場合には、結果は Integer 型になります。オペランドの型が整数型の部分範囲であれば、整数型と同じように扱われます。
  • x div y の値は、x / y の値をゼロの方向に向かって一番近い整数に丸めたものです。
  • mod 演算子は、オペランドを除算した結果の剰余を返します。つまり、
    x mod y = x - (x div y) * y になります。
  • x / yx div yx mod y という形の式で y がゼロであれば、実行時エラーが発生します。

論理演算子

論理演算子 notandorxor は、任意の Boolean 型のオペランドを取り、Boolean 型の値を返します。

論理演算子:

演算子 演算 オペランドの型 結果の型

not

否定

Boolean

Boolean

not (C in MySet)

and

論理積

Boolean

Boolean

Done and (Total >0)

or

論理和

Boolean

Boolean

A or B

xor

排他的論理和

Boolean

Boolean

A xor B

これらの演算は、ブール論理の標準ルールに基づいて行われます。たとえば、x and y という形の式は、xy の両方が True の場合にのみ True になります。

完全論理評価と短絡論理評価

コンパイラは、and 演算子と or 演算子に関して、完全評価および短絡評価(部分評価)という 2 つの評価モードをサポートしています。完全評価では、式全体の結果が既に決定している場合にも、すべてのオペランドを評価します。短絡評価では、厳密に左から右の順で評価を行い、式全体の結果が決定した時点で評価を中止します。たとえば A and B という式を短絡モードで評価したときに AFalse であれば、コンパイラは B を評価しません。A を評価した時点で式全体が False になることがわかるためです。

短絡評価を行うと実行時間が最短になり、また、ほとんどの場合にはコード サイズも最小になるため、通常は短絡評価をお勧めします。完全評価を使用した方がよいのは、オペランドのいずれかが関数であり、プログラムの実行結果に影響する副作用を持っている場合です。

短絡評価では、完全評価を行えば実行時の演算が不正になりかねない構文を使うこともできます。たとえば次のコードは、文字列 S を最初のコンマまで反復処理します。

while (I <= Length(S)) and (S[I] <> ',') do
begin
 ...
 Inc(I);
end;

S にコンマが含まれていない場合、最後の反復で IS の長さより大きい値に増やされます。次に while の条件をテストすると、完全評価では S[I] を読み取ることになり、実行時エラーが生じかねません。逆に短絡評価では、while 条件の前半部分が失敗するため、後半部分の (S[I] <> ',') は評価されません。

評価モードを制御するには $B コンパイラ指令を使用してください。デフォルトの状態は {$B} で、短絡評価が有効になります。ある部分だけで完全評価を有効にするには、コードに {$B+} 指令を追加します。また、プロジェクト全体を完全評価に変更したければ、[<プロジェクト> のプロジェクト オプション]ダイアログの[Object Pascal コンパイラ|コンパイル][完全論理評価]を選択してください。

メモ:オペランドのいずれかがバリアントである場合には、{$B} の状態であってもコンパイラは常に完全評価を行います。

論理(ビット)演算子

次の論理演算子は、整数のオペランドに対してビット単位の操作を行います。たとえば次の文で、X に格納されている値が 2 進法で 001101 であり、Y に格納されている値が 100001 であるとします。

Z := X or Y;

この文では 101101 という値が Z に代入されます。

論理(ビット)演算子:

演算子 演算 オペランドの型 結果の型

not

ビット否定

整数

整数

not X

and

ビット and

整数

整数

X and Y

or

ビット or

整数

整数

X or Y

xor

ビット xor

整数

整数

X xor Y

shl

ビット単位の左シフト

整数

整数

X shl 2

shr

ビット単位の右シフト

整数

整数

Y shr I

ビット演算子には以下のルールが適用されます。

  • not 演算子の結果はオペランドと同じ型になります。
  • andor、または xor 演算のオペランドが 2 つとも整数の場合、結果の型は、両方の型が取り得るすべての値を含む定義済みの整数型のうち、最小の範囲を持つものになります。
  • x shl y および x shr y の演算では、x の値を左または右に y ビットだけシフトします。これは、x が符号なし整数の場合には、x2^y を乗算または除算することと同等であり、結果は x と同じ型になります。たとえば、N01101 という値(10 進数の 13)が格納されているとすると、N shl 111010(10 進数の 26)を返します。y の値が x の型のサイズの剰余として解釈されることに注意してください。たとえば x が整数の場合、整数は 32 ビットであり、40 mod 32 は 8 になるため、x shl 40x shl 8 と解釈されます。

x が負の整数の場合、shl 演算子および shr 演算子は、次の例で明確になります。

 var
   x: integer;
   y: string;
 
 ...
 begin
   x := -20;
   x := x shr 1;
   //As the number is shifted to the right by 1 bit, the sign bit's value replaced is with 0 (all negative numbers have the sign bit set to 1).
   y := IntToHex(x, 8);
   writeln(y);
   //Therefore, x is positive.
   //Decimal value: 2147483638
   //Hexadecimal value: 7FFFFFF6
   //Binary value: 0111 1111 1111 1111 1111 1111 1111 0110
 end.

文字列演算子

関係演算子 =<><><=>= はどれも、文字列のオペランドを取ります(後で説明する「関係演算子」のセクションを参照してください)。+ 演算子は 2 つの文字列を連結します。

文字列演算子:

演算子 演算 オペランドの型 結果の型

+

連結

文字列、パック文字列、文字

文字列

S + '.'


文字列連結には以下のルールが適用されます。

  • + のオペランドは、文字列、パック文字列(Char 型のパック配列)、文字のいずれでも構いません。ただし、一方のオペランドが WideChar 型である場合、もう一方のオペランドは長い文字列(UnicodeStringAnsiString、または WideString)でなければなりません。
  • + 演算の結果は、任意の文字列型と互換性があります。ただし、オペランドが両方とも短い文字列または文字であり、その合計の長さが 255 文字を超える場合、256 文字目以降を切り捨てたものが結果となります。

ポインタ演算子

  • 関係演算子 <><=>= は、PAnsiChar 型および PWideChar 型のオペランドを取ることができます(「関係演算子」のセクションを参照してください)。以下の演算子もポインタをオペランドに取ることができます。ポインタの詳細は、「データ型、変数、定数」の「ポインタとポインタ型(Object Pascal)」を参照してください。

文字ポインタ演算子:

演算子 演算 オペランドの型 結果の型

+

ポインタ加算

文字ポインタ、整数

文字ポインタ

P+I

-

ポインタ減算

文字ポインタ、整数

文字ポインタ、整数

P - Q

^

ポインタ逆参照

ポインタ

ポインタの基底型

P^

=

等しい

ポインタ

Boolean

P = Q

<>

等しくない

ポインタ

Boolean

P <>Q


^ 演算子はポインタを逆参照します。オペランドには任意の型のポインタを取ることができますが、汎用の Pointer の場合だけは逆参照の前に型キャストを行う必要があります。

P = Q は、PQ が同じアドレスを指している場合にのみ True になります。それ以外の場合には P <> QTrue になります。

+ 演算子および - 演算子を使用すると、文字ポインタのオフセットをインクリメントおよびデクリメントすることができます。- は、2 つの文字ポインタのオフセットの差を計算するために使用することもできます。以下のルールが適用されます。

  • I が整数で P が文字ポインタの場合、P + I を実行すると P で指定したアドレスに I が加算されます。つまり、P から I 文字分だけ進んだアドレスのポインタが返されます (I + P という式は P + I と等価です)。P - I を実行すると、P で指定したアドレスから I が減算されます。つまり、P から I 文字分だけ戻ったアドレスのポインタが返されます。ただしこれは PAnsiChar ポインタの場合です。PWideChar ポインタの場合には、P + I を実行すると I * SizeOf(WideChar)P に加算されます。
  • PQ の両方が文字ポインタの場合、P - Q を実行すると、P で指定したアドレス(大きい方のアドレス)と Q で指定したアドレス(小さい方のアドレス)の差が計算されます。つまり、PQ の間の文字数を示す整数が返されます。
P + Q の動作は未定義です。

集合演算子

以下の演算子は集合をオペランドに取ります。

集合演算子:

演算子 演算 オペランドの型 結果の型

+

和集合

集合

集合

Set1 + Set2

-

差集合

集合

集合

S - T

*

積集合

集合

集合

S * T

<=

サブセット

集合

Boolean

Q <= MySet

>=

スーパーセット

集合

Boolean

S1 >= S2

=

等しい

集合

Boolean

S2 = MySet

<>

等しくない

集合

Boolean

MySet <> S1

in

メンバかどうか

序数、集合

Boolean

A in Set1


+-* には以下のルールが適用されます。

  • 序数 OX または Y(あるいはその両方)に含まれる場合にのみ、OX + Y に含まれます。OX に含まれるけれども Y には含まれない場合にのみ、OX - Y に含まれます。OXY の両方に含まれる場合にのみ、OX * Y に含まれます。
  • +-* の演算の結果は、set of A..B 型になります(A は結果セット内の最小の順序値で、B は最大の順序値)。

<=>==<>in には以下のルールが適用されます。

  • X <= Y は、X のすべてのメンバが Y のメンバである場合にのみ True になります。Z >= WW <= Z と等価です。U = V は、UV がまったく同じメンバを含んでいる場合にのみ True になります。それ以外の場合には U <> VTrue になります。
  • O が序数で S が集合のとき、OS のメンバである場合にのみ O in STrue になります。

関係演算子

関係演算子は 2 つのオペランドを比較するために使用します。=<><=>= の演算子は集合にも適用されます。

関係演算子:

演算子 演算 オペランドの型 結果の型

=

等しい

単純型、クラス、クラス参照、インターフェイス、文字列、パック文字列

Boolean

I = Max

<>

等しくない

単純型、クラス、クラス参照、インターフェイス、文字列、パック文字列

Boolean

X <> Y

<

より小さい

単純型、文字列、パック文字列、PChar

Boolean

X < Y

>

より大きい

単純型、文字列、パック文字列、PChar

Boolean

Len > 0

<=

以下

単純型、文字列、パック文字列、PChar

Boolean

Cnt <= I

>=

以上

単純型、文字列、パック文字列、PChar

Boolean

I >= 1


ほとんどの単純型では、比較は簡単です。たとえば、I = JIJ が同じ値を持っている場合にのみ True になり、それ以外の場合には I <> JTrue になります。関係演算子には以下のルールが適用されます。

  • オペランドどうしは互換性のある型でなければなりません。ただし例外として、実数と整数は比較可能です。
  • 文字列は、文字列を構成する文字を構成する順序値に従って比較されます。文字型は長さが 1 の文字列として扱われます。
  • 2 つのパック文字列を比較するには、構成要素の数が同じでなければなりません。n 個の構成要素を持つパック文字列を文字列と比較する場合、パック文字列は長さが n の文字列として扱われます。
  • <><=>= の演算子を使って PAnsiChar(および PWideChar)のオペランドを比較できるのは、2 つのポインタが同じ文字配列の内部を指している場合のみです。
  • = 演算子と <> 演算子は、クラス型およびクラス参照型のオペランドを取ることができます。クラス型のオペランドの場合、= および <> は、ポインタに適用されるルールに従って評価されます。つまり、CD が同じインスタンス オブジェクトを指している場合にのみ C = DTrue になり、それ以外の場合には C <> DTrue になります。クラス参照型のオペランドの場合、CD が同じクラスを示している場合にのみ C = DTrue になり、それ以外の場合には C <> DTrue になります。これは、クラスの中に格納されたデータを比較しているのではありません。クラスの詳細は、「クラスとオブジェクト」を参照してください。

クラス演算子とインターフェイス演算子

as 演算子およびis 演算子は、クラスとインスタンス オブジェクトをオペランドに取ります。as はインターフェイスに対しても使用可能です。詳細は、「クラスとオブジェクト」、「オブジェクト インターフェイス」、「インターフェイス参照」を参照してください。

関係演算子の =<> は、クラスに対しても使用可能です。

@ 演算子

@ 演算子は、変数や、関数、手続き、メソッドのアドレスを返します。つまり、@ によってオペランドを指すポインタを作成できます。ポインタの詳細は、「データ型、変数、定数」の「ポインタとポインタ型」を参照してください。@ には以下のルールが適用されます。

  • X が変数の場合、@XX のアドレスを返します。(X が手続き変数の場合には特別なルールが適用されます。「データ型、変数、定数」の「文と式の中の手続き型」を参照してください。) デフォルトの {$T} コンパイラ指令が有効な場合、@X の型は Pointer になります。{$T+} の状態であれば、@X^T 型(TX の型)になります(この違いは代入の互換性を保つ上で重要です。詳細は「代入の互換性」を参照してください)。
  • F がルーチン(関数または手続き)である場合、@FF のエントリ ポイントを返します。@F の型は常に Pointer です。
  • クラス内に定義されたメソッドに対して @ を適用するには、メソッド識別子をクラス名で修飾する必要があります。以下に例を示します。
@TMyClass.DoSomething
この結果のポインタは、TMyClassDoSomething メソッドを指します。クラスとメソッドの詳細は、「クラスとオブジェクト」を参照してください。

メモ: @ 演算子を使用してインターフェイス メソッドのアドレスを取得することはできません。このアドレスは、コンパイル時には不明であり、実行時に抽出することはできないためです。

演算子の優先順位

複雑な式の演算子がどの順番で実行されるかは、優先順位のルールに従って決まります。

演算子の優先順位

演算子 優先順位

@
not

1 位(最高)

*
/
div
mod
and
shl
shr
as

2 位

+
-
or
xor

3 位

=
<>
<
>
<=
>=
in
is

4位(最低)


優先順位が高い演算子は優先順位が低い演算子よりも先に評価され、演算子の優先順位が同じ場合には左から評価されます。次の例を見てください。

X + Y * Z

この式では、まず YZ を掛け、その結果に X を加えます。*+ よりも優先順位が高いため、先に実行されます。しかし、

X - Y + Z

この式では、まず X から Y を引き、その結果に Z を加えます。-+ は優先順位が同じなので、左側の演算子が先に実行されます。

かっこを使用すると、優先順位ルールと異なる順番で実行させることができます。かっこ内の式がまず評価され、その後は 1 つのオペランドとして扱われます。以下に例を示します。

(X + Y) * Z

この式では、XY の合計に Z を掛けます。

一見必要がなさそうな場所にかっこが必要になる場合もあります。たとえば次のような式があるとします。

X = Y or X = Z

この式は、明らかに次の解釈を意図して書かれています。

(X = Y) or (X = Z)

しかし、かっこがないため、コンパイラは演算子の優先順位ルールに従って、次のように解釈します。

(X = (Y or X)) = Z

その結果、Z が Boolean でなければコンパイル エラーが発生します。

厳密にはかっこが不要な場合でも、かっこを使うことでコードが書きやすく読みやすいものになることがよくあります。最初の例は、次のように書くことができます。

X + (Y * Z)

コンパイラにとっては不要なものですが、このかっこがあることによって、プログラムを書いたり読んだりするときに演算子の優先順位を考慮する必要がなくなります。

関数呼び出し

関数は値を返すため、関数呼び出しも式であるといえます。たとえば、2 つの整数引数を取って整数を返す Calc という関数を定義している場合、Calc(24,47) という関数呼び出しは整数型の式になります。IJ が整数型の変数であれば、I + Calc(J,8) も整数型の式です。関数呼び出しの例には、次のようなものがあります。

Sum(A, 63)
Maximum(147, J)
Sin(X + Y)
Eof(F)
Volume(Radius, Height)
GetValue
TSomeObject.SomeMethod(I,J);

関数の詳細は、「手続きと関数」を参照してください。

集合構成子

集合構成子は集合型の値を示します。以下に例を示します。

[5, 6, 7, 8]

これは、5、6、7、8 というメンバを持つ集合を表します。また、

[ 5..8 ]

この集合構成子も同じ集合を表します。

集合構成子の構文は次のとおりです。

[ item1, ..., itemn ]

各項目は、集合の基底型の序数を表す式と、同様の 2 つの式の間に 2 つのドット(..)をはさんだものとのいずれかです。項目の x..y という形式は、x から y まで(yを含む)のすべての序数を表す簡易表記です。ただし、xy より大きい場合には、x..y[x..y] という集合)は何も含まない空の集合になります。集合構成子の [ ] は空の集合を表し、[x]x という値だけをメンバに持つ集合を表します。

集合構成子の例には、次のようなものがあります。


[red, green, MyColor]
[1, 5, 10..K mod 12, 23]
['A'..'Z', 'a'..'z', Chr(Digit + 48)]

集合の詳細は、「データ型、変数、定数」の「構造化型」を参照してください。

インデックス

文字列や配列や配列プロパティ、および文字列や配列のポインタには、インデックスを使用することができます。たとえば FileName が文字列変数だとすると、FileName[3] という式は FileName が示す文字列の 3 番目の文字を返します。FileName[I + 1]I というインデックスが指す文字の直後の文字を返します。文字列の詳細は、「データ型、変数、定数」を参照してください。配列と配列プロパティの詳細は、「データ型、変数、定数」の「配列型」と、「プロパティ」の「配列プロパティ」のセクションを参照してください。

型キャスト

ある式を別の型の式であるかのように扱えると便利な場合があります。型キャストを行うことで、事実上、式の型が一時的に変更されるため、それが可能になります。たとえば Integer('A') では、A という文字が整数型にキャストされます。

型キャストの構文は次のとおりです。

typeIdentifier(expression)

expression が変数の場合、結果は変数型キャストと呼ばれ、それ以外の場合には、結果は値型キャストと呼ばれます。この 2 つの型キャストは、構文は同じですが適用されるルールが異なります。

値型キャスト

値型キャストでは、型識別子とキャスト式の両方が、順序型またはポインタ型でなければなりません。値型キャストには次のようなものがあります。


Integer('A')
Char(48)
Boolean(0)
Color(2)
Longint(@Buffer)

結果の値は、かっこ内の式を変換することで得られます。その際に、指定された型のサイズと式のサイズとが異なる場合には、切り捨てや拡張が行われることがあります。式の符号は必ず維持されます。

次の例を見てください。

I := Integer('A');

この文では、Integer('A') の値(65)を I という変数に代入しています。

値型キャストの後に限定子を続けることはできません。また、代入文の左辺で値型キャストを使用することもできません。

変数型キャスト

変数は、サイズが同じであり、整数と実数を混在させない限り、任意の型にキャストすることができます。(数値型の変換には、IntTrunc などの標準関数を使用してください。) 変数型キャストには次のようなものがあります。

Char(I)
Boolean(Count)
TSomeDefinedType(MyVariable)

変数型キャストは、代入文の右辺にも左辺にも使用できます。次のコードがあるとします。

var MyChar: char;
  ...
  Shortint(MyChar) := 122;

ここでは、文字 z(ASCII 122)を MyChar に代入しています。

変数を手続き型にキャストすることができます。たとえば、次のように宣言されているとします。

type Func = function(X: Integer): Integer;
var
  F: Func;
  P: Pointer;
  N: Integer;

この場合には、次のような代入を行うことができます。

F := Func(P);     { Assign procedural value in P to F }
Func(P) := F;     { Assign procedural value in F to P }
@F := P;          { Assign pointer value in P to F }
P := @F;          { Assign pointer value in F to P }
N := F(N);        { Call function via F }
N := Func(P)(N);  { Call function via P }

変数型キャストの後には、次の例のように、限定子を続けることもできます。

type
  TByteRec = record
     Lo, Hi: Byte;
  end;
  TWordRec = record
     Low, High: Word;
  end;
  PByte = ^Byte;

var
  B: Byte;
  W: Word;
  L: Longint;
  P: Pointer;

begin
  W := $1234;
  B := TByteRec(W).Lo;
  TByteRec(W).Hi := 0;
  L := $1234567;
  W := TWordRec(L).Low;
  B := TByteRec(TWordRec(L).Low).Hi;
  B := PByte(L)^;
end;

この例では、TByteRec を使ってワードの下位バイトと上位バイトにアクセスし、TWordRec を使って長整数の下位ワードと上位ワードにアクセスしています。Lo および Hi という定義済みの関数を呼び出して同じ目的を達成することもできますが、変数型キャストの方が、代入文の左辺で利用できるという利点があります。

ポインタの型キャストの詳細は、「ポインタとポインタ型(Object Pascal)」を参照してください。クラス型とインターフェイス型のキャストの詳細は、「クラス参照」および「インターフェイス参照」の「as 演算子」のセクションを参照してください。

関連項目