Delphi
Pascal
embarcadero
objectpascal

<11> 手続きと関数 (標準 Pascal 範囲内での Delphi 入門)


11. 手続きと関数

手続き関数はいわゆるサブルーチン (副プログラム) です。手続き (procedure) は実行時に値を返さないルーチンです。関数 (function) は実行時に値を返すルーチンです。

手続き・関数 =

手続き・関数ヘッダ ";" (ブロック | 指令) ";".
手続き・関数ヘッダ =
(手続きヘッダ | 関数ヘッダ).

Delphi だと正確には次のようになっています。

手続き・関数 =

手続き・関数ヘッダ ";" [ブロック ";"].
手続き・関数ヘッダ =
(手続きヘッダ | 関数ヘッダ) [";" 指令指定].
指令指定 =
[指令] [ヒント指令].

手続き・関数はプログラムブロックの手続き・関数宣言部に書かれます。


11.1. 手続き (Procedures)

これまでは宣言済みの手続きを使ってきましたが、この節では手続きの作り方を述べます。

手続き宣言はプログラム部分を定義してそれに名前を関連付ける働きがあります。宣言した手続きは手続き呼び出し文によって実行されます。手続きの宣言は program と同じ形をしていますが、プログラムヘッダの代わりに手続きヘッダで始まります。

手続きヘッダ =

procedure 識別子 [仮パラメータリスト].

手続きの例は次のようになります。


ProcedureTest.pas

program ProcedureTest(output);

var
A: Integer; {*4}
B: Integer; {*4, *5}
procedure Test; {*1}
var {*2}
i: Integer; {*3}
B: Integer; {*3, *5}
begin
B := 0;
for i:=1 to 10 do
B := B + i;
A := A + B;
end; { Test }
begin
A := 1;
B := 2;
Test; { *6 }
Writeln(A);
Writeln(B);
Test; { *6 }
Writeln(A);
Writeln(B);
end.

実行結果は次の通りです。

56

2
111
2

上記のプログラムは簡単なものですが様々な事項が含まれています。



  1. 手続きヘッダ:
    パラメータのない手続き Test() です。


  2. ブロック:
    手続きは名前の付いたブロックです。プログラムブロックは ProcedureTest で、その中に手続きブロック Test があります。手続きのブロックにはプログラムブロックと同じように宣言部があります。


  3. ローカル変数 (局所変数):
    手続き Test() の局所的な変数は iB で、これらの変数は手続き Test() の有効範囲でしかアクセスできません。


  4. グローバル変数 (大域変数):
    変数 AB がメインプログラム (主プログラム) ProcedureTest で宣言された大域的な変数です。これらの変数はプログラムの全域で参照可能で、手続き Test() の中で A に値を代入しています。


  5. 有効範囲 (スコープ):
    変数 i はグローバル変数にもローカル変数にもありますが、それらは同じ変数ではありません。


  6. 手続き呼び出し文:
    メインプログラムが手続き Test()を 2 回実行しています。このようにプログラム部分を手続きにすると、同じ処理を何度も書かずに済みます。

手続き呼び出し文の構文は次の通りです。

手続き呼び出し文 =

手続き名 (実パラメータリスト | 書き出しパラメータリスト).

書き出しパラメータリストWrite() および Writeln() のための特殊な構文です (12.3. 節)。

See also:


11.1.1. パラメータリスト (パラメータ並び / Parameter list)

先のプログラムは手続き内でグローバル変数を直接読み書きしており、計算の回数も変更できなかったので、パラメータを持つように変更してみました。


ProcedureTest2.pas

program ProcedureTest2(output);

var
A: Integer;
B: Integer;
procedure Test(L: Integer; var V: Integer); { *1, *2, *4 }
var
i: Integer;
B: Integer;
begin
B := 0;
for i:=1 to L do
B := B + i;
V := V + B;
end; { Test }
begin
A := 1;
B := 2;
Test(10, A); { *3 }
Writeln(A);
Writeln(B);
Test(20, A); { *3 }
Writeln(A);
Writeln(B);
end.



  1. 手続きヘッダ:
    パラメータリストを持つ形式になっています。


  2. 仮パラメータ (仮引数 / パラメータ / parameter / formal-parameter):
    パラメータリストには仮パラメータの名前とその型を書きます。手続き Test() は仮パラメータとして LV を持っています。
    image.png

  3. 実パラメータ (実引数 / 引数 / argument / actual-parameter):
    手続き呼び出し文には実パラメータのパラメータリストを含んでいます。実パラメータと仮パラメータは同じ並びである必要があります。渡せるパラメータには値パラメータ変数パラメータ手続きパラメータ関数パラメータ の 4 種類があります。Delphi にはこの他にも 定数パラメータout パラメータ等があります。
    image.png


  4. 値パラメータ (Value parameters) と変数パラメータ (Variable parameters):
    パラメータ L は値パラメータであるので、式 (変数を含む) を実パラメータとして渡せます。予約語 var が付加されたパラメータ V変数パラメータであり、手続き内から値を変更する事ができます。


パラメータの種類
予約語
仮パラメータ
(宣言時)
実パラメータ
(呼び出し時)

値パラメータ
(値渡し or 参照渡し1)

変数名

変数パラメータ
(参照渡し)
var
変数名
変数

定数パラメータ
(値渡し or 参照渡し1)

const2

変数名

out パラメータ3
(参照渡し)
out
変数名
変数

仮パラメータリストの定義は次の通りです。各仮パラメータはセミコロン ; で区切ります。

仮パラメータリスト =

"(" 仮パラメータ {";" 仮パラメータ} ")".
仮パラメータ =
[var | CONST | OUT] パラメータ.
パラメータ =
識別子 {"," 識別子} [':' 型名].

実パラメータリストの定義は次の通りです。各実パラメータはカンマ , で区切ります。

実パラメータリスト =

"(" 実パラメータ {"," 実パラメータ} ")".
実パラメータ =
{ [変数 | 式] }.

パラメータのないルーチンの呼び出しでは ( ) を省略できます。

DoSomething();

DoSomething;

See also:


11.1.2. 整合配列パラメータ (Conformant array parameters)

配列型のパラメータをとるルーチンを宣言するとき、そのパラメータ宣言でインデックスの型を指定することはできません。次の宣言はエラーになります。

procedure DoSomething(A: array [1..10] of Integer); 

上記の問題を解決するための仕組みが整合配列パラメータなのですが、Delphi ではこのパラメータ形式をサポートしていません。ISO Pascal 規格においてもオプションです。

整合配列形式 = 

パック状整合配列形式 | アンパック状整合配列形式.
パック状整合配列形式 =
packed array "[" 添字型仕様 "]" of 型名.
アンパック状整合配列形式 =
array "[" 添字型仕様 { ";" 添字型仕様} "]" of (型名 | 整合配列形式).
添字型仕様 =
識別子 ".." 識別子 ":" 順序型名

次の例は任意の長さの文字列 (文字配列) を受け取り、シーザー暗号で出力します。

program CaesarCipher(output);

type
Positive = 1..MaxInt;

procedure CCStr(Str: packed array [M..N: Positive] of Char);
var
i: Integer;
begin
for i := M to N do
Write(Chr(Ord(Str[i]) - 1));
Writeln;
end;

begin
CCStr('IBM');
CCStr('Hello,World.');
end.

実行結果は次のようになります (ideone.com で試せます)。

HAL

Gdkkn+Vnqkc-

ISO Pascal の規格で定義されている言語機能をすべて対応した上で、この整合配列パラメータに対応すれば 標準 Pascal 水準 1 (Standard Pascal Level 1)、対応しなければ 標準 Pascal 水準 0 (Standard Pascal Level 0) となります。

パラメータに直接配列を指定する配列パラメータ (Array parameters) は利用できないのですが、Delphi だと一旦配列型を定義してからパラメータに指定することは可能です。

type 

TDigits = array [1..10] of Integer;

...

procedure DoSomething(A: TDigits);


11.1.3. 再帰手続き (Recursive procedures)

ルーチン内でそのルーチン自身を呼び出すことを再帰呼出し (Recursive call)と言い、それを行う手続きを再帰手続きと呼びます。関数なら再帰関数です。

次のコードは階乗を求める再帰手続きです。


RecursiveProcedureTest.pas

program RecursiveProcedureTest(output);

var
f: Integer;
procedure fact(n: Integer; var v: Integer);
begin
if (n > 0) then
begin
v := v * n;
fact(n - 1, v);
end;
end;
begin
f := 1;
fact(5, f);
Writeln(f);
end.


11.1.4. 手続きパラメータ (Procedural parameters)

Delphi ではこのパラメータ形式をサポートしていません。

パラメータの種類
予約語
仮パラメータ
(宣言時)
実パラメータ
(呼び出し時)

手続きパラメータ

手続きヘッダ
手続き


ProcParamTest.pas

program ProcParamTest(output);

var
A: Integer;
procedure Proc1(V: integer);
begin
A := A + (V * 3);
end; { Proc1 }
procedure Proc2(V: integer);
begin
A := A - (V * 5);
end; { Proc2 }
procedure Test(procedure Proc(V: integer));
begin
Proc(10);
end; { Test }
begin
A := 0;

Test(Proc1);
Writeln(A);

Test(Proc2);
Writeln(A);
end.


実行結果は次の通りです。

 30

-20


(11.1.5.) オープン配列パラメータ (Open Array Parameters)

Delphi では、配列パラメータを実現するための別の解としてオープン配列パラメータが実装されています。

オープン配列パラメータを使用すると、基底型が同じでサイズ (要素数) だけが異なる配列をルーチンに渡す事ができます。

procedure DoSomething(A: array of Integer);

オープン配列パラメータの構文は動的配列の構文に似ていますが同じではありません。実パラメータとして動的配列だけでなく静的配列も渡せます。ルーチンの中では次のようにして渡された配列を処理できます。

procedure Clear(var A: array of Real);

var
I: Integer;
begin
for I := 0 to High(A) do
A[I] := 0;
end;

パラメータに配列そのものを渡しているわけではないため、SetLength() で動的配列の要素数を変更したりする事はできず、

procedure Clear(var A: array of Real);

var
I: Integer;
begin
SetLength(A, 10); // 変更できない
for I := 0 to High(A) do
A[I] := 0;
end;

var
ra: array of Real;

begin
SetLength(ra, 5); // 配列の要素数を 5 に
Clear(ra);
end.

二次元のオープン配列パラメータなんていうものもありません。

procedure Clear(var A: array of array of Real); // NG

オープン配列パラメータを使ったルーチンには、オープン配列コンストラクタも渡すことができます。

See also:


(11.1.6.) 型可変オープン配列パラメータ (Variant Open Array Parameters)

型可変オープン配列パラメータ (型なしの配列パラメータ) を使用すると、基底型すら異なる配列を渡す事ができます。

procedure DoSomething(A: array of const);

array of constarray of TVarRec と同じなので、TVarRec.VType を使って型ごとに処理を分岐させる事ができます。

function MakeStr(const Args: array of const): string;

var
I: Integer;
begin
Result := '';
for I := 0 to High(Args) do
with Args[I] do
case VType of
vtInteger: Result := Result + IntToStr(VInteger);
vtBoolean: Result := Result + BoolToStr(VBoolean);
vtChar: Result := Result + VChar;
vtExtended: Result := Result + FloatToStr(VExtended^);
vtString: Result := Result + VString^;
vtPChar: Result := Result + VPChar;
vtObject: Result := Result + VObject.ClassName;
vtClass: Result := Result + VClass.ClassName;
vtAnsiString: Result := Result + string(VAnsiString);
vtUnicodeString: Result := Result + string(VUnicodeString);
vtCurrency: Result := Result + CurrToStr(VCurrency^);
vtVariant: Result := Result + string(VVariant^);
vtInt64: Result := Result + IntToStr(VInt64^);
end;
end;

型可変オープン配列パラメータにもオープン配列コンストラクタを渡すことができます。

See also:


(11.1.7.) オープン配列コンストラクタ (Open Array Constructors)

オープン配列コンストラクタにより、関数および手続き呼び出し内で、直接、配列を構築することができます。それらは、オープン配列パラメータまたは、型可変オープン配列パラメータとしてのみ渡すことができます。

例えば次のような定義 (オープン配列パラメータ) があった場合、

procedure Add(A: array of Integer);

オープン配列コンストラクタを使って Add() 手続きを次の文で呼び出すことができます:

Add([5, 7, I, I + J]);

次のような定義 (型可変オープン配列パラメータ) なら、

procedure Add(A: array of const);

オープン配列コンストラクタを使って Add() 手続きを次の文で呼び出すことができます:

Add([5, 7, I, I + J]);

Add([100, 'A', 3.14]);

オープン配列コンストラクタを頻繁に使う最も有名なルーチンは System.SysUtils にある Format() 関数です。

  uses

..., System.SysUtils;

s := Format('%s: %.4d', ['Hello', 123]);

See also:


(11.1.8.) デフォルトパラメータ (Default Parameters)

手続きや関数のヘッダー (仮パラメータ) で、デフォルトパラメータ値を指定できます。デフォルト値を指定できるのは、値パラメータと定数パラメータだけです。

たとえば、次のような宣言は

procedure FillArray(A: array of Integer; Value: Integer = 0);

次のいずれの呼び出しでも結果は同じになります。

FillArray(MyArray);

FillArray(MyArray, 0);

複数のパラメータを一度に宣言する場合には、デフォルト値は指定できません。 従って、次の宣言は無効です。

function MyFunction(X, Y: Real = 3.5): Real;  // syntax error

デフォルト値を指定するパラメータは、パラメータ リストの末尾に置かなければなりません。 つまり、あるパラメータにデフォルト値を指定したら、それ以降のパラメータにはすべてデフォルト値を指定する必要があります。 従って、次の宣言は無効です。

procedure MyProcedure(I: Integer = 1; S: string);  // syntax error

See also:


(11.1.9.) ルーチンのオーバーロード (Overloading Routines)

同じスコープ内で同じ名前のルーチンを複数宣言することができます。これは、オーバーロードと呼ばれます。オーバーロードされるルーチンは、overload 指令を付けて宣言する必要があり、パラメータ リストで区別できなければなりません。

 function Divide(X, Y: Real): Real; overload;

begin
Result := X / Y;
end

function Divide(X, Y: Integer): Integer; overload;
begin
Result := X div Y;
end;

オーバーロードされたルーチンは、受け取るパラメータの数またはそれらのパラメータの型で区別できなければなりません。

See also:


(11.1.10.) オープン文字列パラメータ (Open String Parameters)

Delphi のオープン文字列パラメータは、ANSI 版 Delphi かつ String が短い文字列 (ShortString) である ({$H-}) 場合にのみ意味を持ちます。

次に示すコードのパラメータ S の string はString 型を示しているのではなく、オープン配列パラメータの文字列版である オープン文字列パラメータ を示しています。長さの異なる文字列変数をルーチンのパラメータとして渡す事ができます。

program OpenStringParameterTest(Output);

{$APPTYPE Console}

var
S1: string[10];
S2: string[20];

procedure AssignStr(var S: string);
begin
S := '0123456789ABCDEF';
end; { AssignStr }

begin
AssignStr(S1); { '0123456789' }
AssignStr(S2); { '0123456789ABCDEF' }
end.

コンパイラ指令 {$P} はパラメータの string をオープン文字列パラメータとみなすかどうかのスイッチです。デフォルトで{$P+} ですが、{$P-} だと string パラメータはただの変数パラメータとみなされます。

Delphi 1 では上記コードのコンパイルが通ったのですが、Delphi 2 以降では、コンパイルエラーになりました。S1 と S2 が 短い文字列 (ShortString) であるのに対し、S はオープン文字列パラメータではなく長い文字列 (string) の変数パラメータだからです。

この問題を解決するためには 2 つの方法があります。一つはコンパイラ指令 {$H-} を使い、string 型を短い文字列 (ShortString) とみなすようにする方法です。影響範囲が広いですが、大量のコードを移植しなければならない時には有効でしょう。

もう一つは OpenString 識別子を使う方法です。OpenString は正確には型ではなく、オープン文字列パラメータを示す識別子で、次のコードでは、パラメータ S はオープン文字列パラメータとして認識されます (型としては ShortString)。

  procedure AssignStr(var S: OpenString);

begin
S := '0123456789ABCDEF';
end; { AssignStr }

”String” という識別子を使っていないため、コンパイラ指令 {$P} の影響も受けません。

コンパイラ指令 {$V} というのもあります。このオプションは本質的に "安全でないバージョンのオープン文字列パラメータ" を提供します。{$P+} が指定されている場合には、string パラメータはオープン文字列パラメータですので、{$V} の効果はありません。

program OpenStringParameterTest2(Output);

{$APPTYPE Console}

procedure MyProc(var s: string);
begin
s := 'abcdefghijk';
end; { MyProc }

var
ss: string[5];
begin
MyProc(ss);
end.

上記コードと {$P} および {$V} コンパイラ指令の関係は次の通りです。

{$P}
{$V}
説明

{$P+}
{\$V+}
or
{\$V-}
オープン文字列パラメータ。
S への割り当てが実パラメータで宣言された
サイズを超えないことを保証する。

{$P-}
{$V+}
MyProc(ss) は型不一致の
コンパイルエラーを生成する。

{$P-}
{$V-}
MyProc はコンパイラエラーを生成しないが、
プログラム内で上書きエラーになり、
システムがクラッシュする可能性がある。

String が短い文字列 だったのは Delphi 1 とそれ以前の Turbo Pascal ですから、これらのコンパイラ指令はもはや気にする必要がないと思います。

むしろ Unicode 版 Delphi になってずいぶん経ちますが、未だにコンパイラ指令 {$H}{$P}{$V} がプロジェクトオプションに存在するのがとても不思議です。

See also:


11.2. 関数 (Functions)

関数宣言はプログラム部分を定義してそれに名前を関連付ける働きがあります。宣言した関数は関数呼び出しによって実行されます。関数宣言は program とほぼ同じ形をしていますが、プログラムヘッダの代わりに関数ヘッダで始まります。

関数ヘッダ =

functon 識別子 [仮パラメータリスト] ":" 型名.

ほぼ手続きと同じですが、関数は値を返す (結果 / 戻り値) ため、関数呼び出しは、代入や演算子内の式として使用できます。


結果型 (Result Type)

結果の型 (結果型) は単純型またはポインタ型の識別子である必要があります。標準 Pascal では結果型に構造化型は使えません。Delphi だと一旦型を定義すれば結果型に構造化型が使えます。


ResultTest.pas

program ResultTest;

type
Str10 = array [1..10] of Char;
TIDPass =
record
ID: Integer;
Pass: Str10;
end;

// NG
function Sub1: array [1..10] of Char;
begin
...
end;

// OK
function Sub1: Str10;
begin
...
end;

// NG
function Sub2: record ID: Integer; Pass: Str10 end;
begin
...
end;

// OK
function Sub2: TIDPass;
begin
...
end;

begin

end.



結果 (Result)

Pascal では関数が返す値を結果 (result)と呼びます。他の言語では戻り値 (return value)と呼ばれる事が多いですが、当記事では由緒正しく結果と表記しています。

関数で結果を返すには、標準 Pascal だと関数名に対して値を代入します。関数名は、その関数の結果を保持する特殊な変数として使用できます。

function Add(A, B: integer): Integer;

begin
Add := A + B;
end;

Delphi では拡張構文 {$X} がデフォルトで有効 ({$X+}) になっているので、関数ブロックで定義済みの変数 Result を使って、関数の結果を保持することができます。Result 変数のスコープは関数ブロック内です。

function Add(A, B: integer): Integer;

begin
Result := A + B;
end;

Delphi 2009 以降では Exit() 手続きで、関数の結果をパラメータで受け取れるようになりました。C 言語の return 文のような記述が可能になっています。

function Add(A, B: integer): Integer;

begin
Exit(A + B);
end;

パラメータを受け取る Exit() 手続き自体は UCSD Pascal でも実装されていましたが、受け取るパラメータはルーチンでした。逆にパラメータのない Exit() はありませんでした。

function Add(A, B: integer): Integer;

begin
Add := A + B;
Exit(Add);
end;

拡張 Pascal では結果を保持する変数名を指定できます。ユーザー定義の Result 変数のようなものです。

function Add(A, B: integer) = i: Integer;

begin
i := A + B;
end;

See also:


関数呼び出し (Function Call)

4.2. 節 で説明した通り、標準 Pascal は結果が不要でも結果を利用しない関数は呼び出せません。つまり、標準 Pascal での関数は必ず式の中で使われるため、関数呼び出し文というのはありませんでした。Delphi では結果を破棄できるため、関数呼び出し文が成立します。

また、パラメータのない関数の呼び出しでは ( ) を省略できます。

v := DoSomething();

v := DoSomething;

See also:


再帰関数 (Recursive functions)

次のコードは階乗を求める再帰関数です。


RecursiveFunctionTest.pas

program RecursiveFunctionTest(output);

var
f: Integer;
function fact(n: Integer): Integer;
begin
if (n = 0) then
fact := 1
else
fact := fact(n - 1) * n;
end;
begin
f := fact(5);
Writeln(f);
end.

関数名が代入文の左辺にある場合、コンパイラはそれを結果を記録するために使われているものと見なします。関数名がステートメントブロックのその他の場所にある場合、コンパイラはそれをその関数の再帰呼び出しと解釈します。

結果を返すのに Result 変数を使うと再帰呼び出しなのかそうでないのかを判別しやすくなります。


RecursiveFunctionTest.pas

program RecursiveFunctionTest(output);

var
f: Integer;
function fact(n: Integer): Integer;
begin
if (n = 0) then
Result := 1
else
Result := fact(n - 1) * n;
end;
begin
f := fact(5);
Writeln(f);
end.


11.2.1. 関数パラメータ (Functional parameters)

Delphi ではこのパラメータ形式をサポートしていません。手続きパラメータと同様なので省略します。

パラメータの種類
予約語
仮パラメータ
(宣言時)
実パラメータ
(呼び出し時)

関数パラメータ

関数ヘッダ
関数


11.2.2. 副作用 (Side effect)

J&W に掲載されていた、手続きや関数内からグローバル変数を操作すると思わぬ結果をもたらす、というコードのサンプルです。結果を予想するのがとても難しいです。


SideEffect.pas

program SideEffect(Output);

{ プログラム 11.10 - 関数の副作用の例 }

var
A, Z: Integer;

function Sneaky(X: Integer): Integer;
begin
Z := Z - X { Z への副作用 };
Sneaky := Sqr(X);
end; { Sneaky }

begin
Z := 10; A := Sneaky(Z);
Writeln(Output, A, Z);
Z := 10; A := Sneaky(10); A := A * Sneaky(Z);
Writeln(Output, A, Z);
Z := 10; A := Sneaky(Z); A := A * Sneaky(10);
Writeln(Output, A, Z);
end. { SideEffect }


上記コードの実行結果は次の通りです。

        100          0

0 0
10000 -10


11.3. 前方宣言 (Forward declaration)

手続きや関数は前方宣言があれば、その手続きや関数の宣言よりも前で使用できます。

  procedure A(v: Integer);

begin
B(1); { 呼び出せない }
end;

procedure B(v: Integer);
begin

end;

上記の問題は Forward 指令を使って前方宣言する事によって回避できます。

  procedure B(v: Integer); Forward;

procedure A(v: Integer);
begin
B(1); { 呼び出せる }
end;

procedure B; { 仮パラメータと結果型はここに書かない }
begin

end;

Delphi の場合、前方宣言はプログラムファイル (*.dpr) 内だけで使えます。

forward 指令は、ユニットの interface セクションでは無効です。interface セクション内の手続きと関数のヘッダーは、forward 宣言と同様の動作をするので、implementation セクションで定義宣言をする必要があります。


Unit1.pas

unit Unit1;

interface

// 関数のヘッダー宣言
procedure A(v: Integer);
procedure B(v: Integer);

implementation

procedure A(v: Integer);
begin
B(1); { 呼び出せる }
end;

procedure B(v: Integer);
begin

end;

end.


See also:


(11.4.) 手続き型定数とグローバル手続き型変数の初期化

Delphi で手続き型定数を宣言するには、定数の宣言される手続き型と互換性のあるルーチンの名前を指定します。


ProceduralConstantsTest1.pas

program ProceduralConstantsTest1;

{$APPTYPE CONSOLE}

// 手続き型の定義
type
TFunction = function(X, Y: Integer): Integer;

var
i: Integer;

function Calc(X, Y: Integer): Integer;
begin

end; { Calc }

// グローバル手続き型定数
const
MyFunction1: TFunction = Calc;

// グローバル手続き型変数と初期化
var
MyFunction2: TFunction = Calc;

procedure Sub;
//var
// ローカル手続き型変数は初期化できない
// MyFunction3: TFunction = Calc;
begin

end; { Sub }

begin
i := MyFunction1(5, 7);
i := MyFunction2(5, 7);
end.


手続き型を定義せずに直接宣言する事もできます。


ProceduralConstantsTest2.pas

program ProceduralConstantsTest2;

{$APPTYPE CONSOLE}

var
i: Integer;

function Calc(X, Y: Integer): Integer;
begin

end; { Calc }

const
MyFunction1: function(X, Y: Integer): Integer = Calc;

// グローバル手続き変数と初期化
var
MyFunction2: function(X, Y: Integer): Integer = Calc;

procedure Sub;
//var
// ローカル手続き変数は初期化できない
// MyFunction3: function(X, Y: Integer): Integer = Calc;
begin

end; { Sub }

begin
i := MyFunction1(5, 7);
i := MyFunction2(5, 7);
end.


手続き型変数を使うと次のような事ができます。


ProceduralConstantsTest2

program ProceduralTypesFizzBuzz;

{$APPTYPE CONSOLE}

procedure FizzBuzz(X: Integer);
begin
Writeln('FizzBuzz');
end; { FizzBuzz }

procedure Fizz(X: Integer);
begin
Writeln('Fizz');
end; { Fizz }

procedure Buzz(X: Integer);
begin
Writeln('Buzz');
end; { Buzz }

procedure Num(X: Integer);
begin
Writeln(X);
end; { Num }

var
i: Integer;
FB: procedure(X: Integer);
begin
for i:=1 to 100 do
begin
if ((i mod 3) + (i mod 5)) = 0 then
FB := FizzBuzz
else if (i mod 3) = 0 then
FB := Fizz
else if (i mod 5) = 0 then
FB := Buzz
else
FB := Num;
FB(i);
end;
end.


See also:


索引

[ ← 10. ポインタ型 ] [ ↑ 目次へ ] [ → 12. テキストファイルの入出力 ]





  1. パラメータの型とサイズに応じて値渡しまたは参照渡しになります。複雑なのでヘルプで確認してください。基本的には型のサイズが SizeOf(Pointer) よりも大きければ参照渡しとなります。 



  2. Delphi XE3 以降では、定数パラメータに [Ref] 属性を付けて参照渡しを強制させる事ができます。属性の指定は const [Ref][Ref] const どちらでも構いません。 



  3. out パラメータは Component Object Model (COM) 以外の用途で使われることはあまりありません。 var の使用が推奨されています。