関数

うまくなろうとしたときはいつでもだが、大きな前進をする前に、必ずちょっとよろめいて後退するという経験をしたのである。

『スーパーエンジニアへの道-技術リーダーシップの人間学』G.M.ワインバーグ著、木村泉訳より引用

これまでは関数はただ利用するだけだったが、ここでは関数を定義する方法をみる。

Add という名前の関数を定義しよう。Add は 2 つの整数を引数にとり、その引数を足したものを返す。

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
end;

var
  x: Integer;
begin
  x := Add(10, 2);
  Writeln(x);
  Readln;
end.
12

関数の定義は、次のようになる。

function <関数名>(<引数>): <戻り値の型>;
begin
  <文>
end;

これを Add 関数に当てはめると、

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
end;

<関数名> は Add, <引数>は

x, y: Integer

であるから引数は 2 つとることを意味する。それぞれ名前は x と y でどちらの型も Integer である。そして <戻り値の型> は Integer である。もう一度関数定義を見直そう:

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
end;
Add(10, 2); 

という関数 Add の呼び出しがあるとき、引数は 10 と 2 である。関数が呼び出されると、引数として定義された変数にそれぞれ値が格納される。つまり引数として定義された x と y にそれぞれ 10 と 2 が格納される。それから関数の本体が実行される。

x := Add(10, 2);

このコードの実行手順を図にすると次のようになる。

Result は変数である。この変数 Result は、

という特徴をもつ。たとえば、関数 Add の場合、変数 Result の型は Integer となる。Result に代入された値が、その関数の戻り値となるので、関数 Add は x と y を足し算したものが、その戻り値となる。

関数を呼び出すとき、その関数が受け取る引数の数、それぞれの引数の型が一致していなくてはならない。引数の数が一致しないとエラーになる:

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
end;

begin
  Add(1); // エラー

  Readln;
end.
[エラー] 実パラメータが足りません

Add 関数は 2 つの引数を受け取るので、引数 1 つでこの関数を呼び出すことはできない。

引数の型が異なってもエラーとなる:

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
end;

begin
  Add(1, 'foo'); // エラー

  Readln;
end.
[エラー] 'Integer' と 'String' には互換性がありません

Add は引数として 2 つの整数型の値を受け取る。文字列は受け取れないのでエラーとなる。

次に、引数をとらない関数を定義してみよう。

function Return10: Integer;
begin
  Result := 10;
end;

var
  x: Integer;
begin
  x := Return10;
  Writeln(x);

  x := Return10 + Return10;
  Writeln(x);

  Readln;
end.
10
20

関数 Return10 を呼び出すと、10 が返される。この関数は引数をとらない。引数をとらない関数の定義の仕方、および関数の呼び出し方は上の通り。

いきなりだが、あなたの名前をたとえば佐藤太郎さんということにしよう。偶然なことに、となりの家も佐藤さんだ。となりの佐藤さんは新婚さんで、今年の春、赤ちゃんが誕生した。これまた偶然なことにその赤ちゃんの名前もあなたと同じ太郎ちゃんだ。あなたも佐藤太郎だし、となりの赤ちゃんも佐藤太郎ちゃんということになる。しかし、おなじ名前ではあるが「おなじ」ではない。

ここで言いたいのは、変数の名前についてである。たとえば、次のようなコードがあるとする:

var
  x: Integer;
  x: string;
begin
  Readln;
end.
[エラー] 識別子の多重定義 'x'

エラーメッセージが示すように、同じ名前の変数を複数宣言することはできない。

ところで、関数の中でも変数は宣言できる。それぞれの関数の中で宣言された変数は、他で宣言された変数とは別物扱いされる:

function Foo(x: Integer): Integer;
var
  z: Integer;
begin
  z := 30;
  Result := x + z;
end;

var
  z: Integer; // *1*
begin
  z := 120;
  Writeln(Foo(z));
  Writeln(z);
  Readln;
end.
150
120

*1* のところで z という変数を宣言している。関数 Foo の中でも変数 z を宣言している。これら 2 つの変数は同じではなく区別される。つまり、ある関数の中で宣言された変数は、他の関数などで宣言された変数とは区別されるということだ。ちょうど、あなたととなりの赤ちゃんが名前は同じでも区別されるように。

次に、値を返さない関数について見ていこう。値を返す関数との違いはとくに何もない。たんに値を返さないということでしかない。たとえば、引数をプリントする「値を返さない関数」は次のように書ける。

procedure Print(x: Integer);
begin
  Writeln(x);
end;

begin
  Print(12);
  Print(100);
  Readln;
end.
12
100

値を返さない Print 関数を定義した。値を返す関数と、上記の値を返さない関数を定義するときの違いは、function となる所が procedure に変わり、値を返さないので関数の戻り値の型を書かなくなっただけである。当然、function では暗黙に宣言されていた変数 Result も、値を返さない関数では存在しない。

値を返さないので、関数からの戻り値を代入するようなコードを書くとエラーになる:

procedure Print(x: Integer);
begin
  Writeln(x);
end;

var
  x: Integer;
begin
  x := Print(12); // エラー
  Readln;
end.
[エラー]'Integer' と '関数、型無しポインタまたは型無しパラメータ'には互換性がありません

エラーメッセージがなにやら複雑だが、要は、値を返さないのだから関数の戻り値を代入するようなコードは書けないということだ。

Delphi のヘルプやマニュアルには値を返さない関数のことを特に「手続き」と呼ぶようなことが書いてあるが、別に値を返そうが返すまいがどちらも関数と呼んでも構わない。

ここで、関数をいくつか書いてみよう。次の関数は、2 つの整数型の引数をとり、1 番目の値から 2 番目の値までの数の合計を返す関数である。

function Sum(x, y: Integer): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := x to y do
    Result := Result + i;
end;

begin
  Writeln(Sum(1, 3));
  Writeln(Sum(1, 10));
  Writeln(Sum(5, 9));
  Readln;
end.
6
55
35

次の関数は、上の関数の足し算を掛け算にしたものである。

function Fac(x, y: Integer): Integer;
var
  i: Integer;
begin
  Result := 1;
  for i := x to y do
    Result := Result * i;
end;

begin
  Writeln(Fac(1, 3));
  Writeln(Fac(1, 10));
  Writeln(Fac(5, 9));
  Readln;
end.
6
3628800
15120

次のは、累乗を返す関数だ。

function Pow(x, y: Integer): Integer;
var
  i: Integer;
begin
  Result := x;
  for i := 2 to y do
    Result := Result * x;
end;

begin
  Writeln(Pow(3, 1));
  Writeln(Pow(3, 2));
  Writeln(Pow(3, 3));
  Writeln(Pow(3, 4));

  Readln;
end.
3
9
27
81

次の関数は、2 つの引数をとる。1 番目は文字列型をとり、2 番目は整数型をとる。この関数は 引数の 1 番目の文字列を 2 番目の整数の数だけつなげた文字列を返す。

function Concat(s: string; n: Integer): string;
begin
  Result := '';
  while n > 0 do
  begin
    Result := Result + s;
    Dec(n);
  end;
end;

begin
  Writeln(Concat('Foo', 2));
  Writeln(Concat('Hey ', 3));
  Writeln(Concat('-', 5));

  Readln;
end.
FooFoo
Hey Hey Hey
-----

Dec 関数は、引数を 1 だけ減算する。

var
  x: Integer;
begin
  x := 10;
  Writeln(x);
  Dec(x);
  Writeln(x);
  Dec(x);
  Writeln(x);
  Dec(x);
  Writeln(x);
end.
10
9
8
7

次の関数は、文字列を受け取り、それを逆にして返す。

function Reverse(s: string): string;
var
  Len: Integer;
begin
  Result := '';
  Len := Length(s);

  while Len > 0 do
  begin
    Result := Result + s[Len];
    Dec(Len);
  end;
end;

begin
  Writeln(Reverse('string'));
  Writeln(Reverse('eye'));
  Writeln(Reverse('tea'));

  Readln;
end.
gnirts
eye
aet

あなたへの課題:

Reverse 関数を、次のコードを参考にして for 文を使って書き直してみよう。

var
  s: string;
  i: Integer;
begin
  s := 'orange';
  Writeln(Length(s));

  s := 'air';
  Writeln(Length(s));

  Writeln('---');

  for i := 5 downto 0 do
    Writeln(i);

  Readln;
end.
6
3
---
5
4
3
2
1
0

上のコードのポイントは次のとおり:

次の関数は、2 つの引数をとる。引数の 1 番目の文字列の中から、2 番目に指定された文字を取り除いた文字列を返す。

function Remove(s: string; c: Char): string;
var
  Idx: Integer;
begin
  Result := '';
  Idx := 1;

  while Idx < (Length(s) + 1) do
  begin
    if s[Idx] <> c then
      Result := Result + s[Idx];
    Inc(Idx);
  end;
end;

begin
  Writeln(Remove('string', 'r'));
  Writeln(Remove('language', 'a'));
  Writeln(Remove('level', 'l'));

  Readln;
end.
sting
lnguge
eve

Inc 関数は引数を 1 だけ加算する。

var
  x: Integer;
begin
  x := 5;
  Writeln(x);
  Inc(x);
  Writeln(x);
  Inc(x);
  Writeln(x);
end.
5
6
7

あなたへの課題:Remove 関数を for 文を使って書き直してみよう。

ヘルプで関数を調べる

Delphi ヘルプの使いかたを解説する。例として Max 関数をヘルプで引いてみることにしよう。Max 関数へのヘルプ表示までは、いく通りかの辿り方がある:

どちらの操作でも、同じ Max 関数のヘルプにたどり着くことが出来る。ヘルプを引くのは初めてなので、ここではそれぞれのやり方について見ていきたい。

エディタに Max という語がある。その上にカーソル(マウスカーソルではない)がのっているのが分かるだろう。図ではちょうど "x" の上にカーソルがのっている。この状態で F1 キーを押すと、次のダイアログが表示される。

"Max 関数(VCL Reference)"を選択し、表示ボタンを押すと、下図の Max 関数についてのヘルプが表示されるはずだ。

メニュー欄からヘルプを引く場合は、メニューの [ ヘルプ | Delphi ヘルプ ] を選択する。

表示されたダイアログの「キーワード」タブを選択する。上図のようにキーワード入力欄に Max とタイプすると、2. の一覧に「Max 関数」という項目があらわれる。それをマウスで選択することで、同様に Max 関数についてのヘルプが表示される。

あなたへの課題の解答例

function Reverse(s: string): string;
var
  i: Integer;
begin
  Result := '';
  for i := Length(s) downto 1 do
    Result := Result + s[i];
end;
function Remove(s: string; c: Char): string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to Length(s) do
    if s[i] <> c then
      Result := Result + s[i];
end;

up next

更新日:2005-01-15