• 検索結果がありません。

関数定義の例

ドキュメント内 新潟大学学術リポジトリ (ページ 144-148)

まず手始めに、簡単な例題から始めよう。

例題 10.1 (二項係数の計算) n個のものからk個を選ぶ組合せの数 nk は n

k

= n!

k! (n−k)!

と計算できる。2つの正整数データnとk を読み込みこの計算式に基づいて組合せの 数 nk

を計算して出力するCプログラムを作成せよ。

(考え方) 計算式に階乗計算が3箇所もあるので、main()関数とは別に、階乗計算を行

う関数 factorial() を定義するのが自然であろう。 引数として整数値を受け取りその階

乗値を返す関数factorial() が記述されていれば、組合せの数 nk

の計算は、数学関数 と同じ様にfactorial() を呼び出して

n k

= factorial(n)

factorial(k) factorial(n−k)

という風に行うことができる。関数 factorial() に与える引数データは、我々が入力す る正整数、およびそれらの差であるので、そのデータ型は int とするのが妥当である。

10.1. 関数定義の例 139

また、factorial()の関数値は本来整数であるが、int型で表せる範囲を越えてしまう危 険性もあるので、そのデータ型を実数型にして階乗値も組合せの数も近似計算する方が無 難である。 階乗計算については、例題7.6と同じ風に行えば良い。

(プログラミング) 階乗計算を行う関数 factorial() は、引数としてint 型のデータを 受け取り、関数値としてdouble型のデータを返すものとする。関数factorial() の中で は、引数として受け取るデータを格納するために k という名前のint型変数を、1!,2!,3!,...

の値を保持するためにfactという名前のdouble変数を、そしてfor文による繰り返しを 制御するために i という名前のint型変数を用意する。 また、main()関数の中では、

読み込んだ正整数 nとk を格納するために各々 n と k という名前のint型変数を用意し てプログラムを構成した。このCプログラムと、これをコンパイル/実行している様子を 次に示す。(下線部はキーボードからの入力を表す。)

[motoki@x205a]$ nl binomial-coeff.c Enter

1 /* 2つの正整数データ n と k を読み込み */

2 /* 二項係数 n!/(k!*(n-k)!) を出力するCプログラム */

3 #include <stdio.h>

4 double factorial(int k);

5 int main(void) 6 {

7 int n, k;

8 printf("It will compute a binomial coefficient.\n"

9 "Input two positive integers n and k(<=n): ");

10 scanf("%d%d", &n, &k);

11 printf("\nThe number of the combinations of\n"

12 " n objects taken k at a time = %20.14g\n", 13 factorial(n)/(factorial(k)*factorial(n-k)));

14 return 0;

15 }

16 /*---*/

17 /* 階乗値を計算してその結果を返す関数 */

18 /*---*/

19 /* (仮引数) k : 何の階乗を計算するかを表す整数 */

20 /* (関数値) : k! の値をdoubleで */

21 /*---*/

22 double factorial(int k) 23 {

24 int i;

25 double fact;

26 fact = 1.0;

27 for (i=2; i<=k; ++i) 28 fact *= (double)i;

29 return fact;

30 }

[motoki@x205a]$ gcc binomial-coeff.c Enter [motoki@x205a]$ ./a.out Enter

It will compute a binomial coefficient.

Input two positive integers n and k(<=n): 50 25 Enter The number of the combinations of

n objects taken k at a time = 1.2641060643775e+14 [motoki@x205a]$

ここで、

• プログラムの4行目 は、22〜30行目で定義した関数 factorial() がどういう型の引 数を受け取りどういう型の値を返すのかを宣言した文で、関数プロトタイプ(または 関数原型)と呼ばれる。4行目には k という名前の変数名が書かれているが、これは 省略可能で、これによって変数領域の確保を指示している訳ではない。一般に関数プ ロトタイプは次のような構造をしている。

関数値のデータ型 関数名 ( データ型 名前, . . . , データ型 名前 );

または

関数値のデータ型 関数名 ( データ型 , . . . , データ型 );

コンパイラはプログラムを前から順に見て行くので、この宣言が無いとコンパイラ は13行目を見る時点で、呼び出された関数 factorial()の引数として計算したもの をどういうデータ型に変換して factorial() に引き渡せば良いかも分からないし、

factorial() の計算結果として返って来たデータをどういう風に解釈すれば良いか

も分からない。#includeの指令は、大抵の場合、printf() や scanf() といった標 準ライブラリ関数についての、関数プロトタイプを読み込むのが目的となっている。

• プログラムの5〜15行目は関数main()を定義した部分、22〜30行目 は関数factorial() を定義した部分になっている。関数値の型の宣言が省略された場合は、int型が暗黙 に仮定される。

• プログラムの7行目 にも22行目 にも kという名前の変数が確保されているが、これ らは別の変数として扱われる。 実際、8〜14行目で変数 k を使うと7行目で確保し た変数 k として扱われ、23〜29行目で変数 k を使うと22行目で確保した変数 k と して扱われる。

• プログラムの13行目 では関数factorial() が3回呼び出されている。関数が呼び出 されると、関数に引き渡されたデータの値を記憶する変数 k、および関数本体の最初 に宣言されている変数i のための領域が 新たに動的に 確保される。

• 一般に、呼び出しの際に関数名の後の丸括弧の中で指定し、関数に実際に引き渡す データのことを実引数と呼ぶ。これに対し、実引数の値を記憶するために関数の中に

10.1. 関数定義の例 141

用意される変数のことを仮引数と呼ぶ。

• プログラム29行目 の return 文は、関数の実行を終了し factorial()の計算結果

(関数値) として式fact の値を呼び出し元に返すことを表す。

'

&

$

% 補足:関数の呼び出し元に戻される値は関数の計算結果を表すので、これまでこの値の

ことを関数値と呼んできたが、C言語では通常この値のことを戻り値(あるいは 返却値)と呼ぶ。

• プログラムの16〜21行目 は関数 factorial() の仕様、すなわちこの関数を呼び出 す側に対してどういう機能を提供するかを明確に記述した注釈である。

'

&

$

% 関数の仕様を記述することの利点:

各々の関数に仕様が書かれていると、作り上げた関数の処理内容を理解する際、

その関数から呼び出す別の関数については処理内容を詳しく見る代わりに仕様を 見るだけで良いので、一度に把握するプログラムの範囲が小さくて済む。

=3 プログラムを理解し易くなる。

3 多数の関数が複雑に絡み合った大きなプログラムを作る場合もしっかり としたプログラムを作ることが可能となる。

関数仕様の書き方について:

仕様としては、関数を使う側に対してどういう機能を提供するのかを書く。

それゆえ、

3 与えられた引数に対して、どういう関数値が返されるかを書く。

3 関数の外側で確保された変数等の値を変える作用、いわゆる副作用がある 場合は、どういう副作用があるかも書く。

3 関数の内部の細かな変数や、処理手順についての記述は控えるべきである。

□演習 10.2 (円錐の体積) 2つの実数データ r と h をパラメータとして受け取り、底面 の半径が rで高さがh の円錐の体積を計算結果として返す関数を定義することによって、

例題7.1のプログラムを作り直してみよ。

□演習 10.3 (最大公約数) 2つの正整数をパラメータとして受け取りその最大公約数を

計算結果として返す関数を定義することによって、例題6.12のプログラムを作り直して みよ。

□演習 10.4 (素数かどうかの判定) 正整数を1個パラメータとして受け取り、それが素

数かどうかを判定してその結果を返す関数を定義することによって、例題6.16のプログ ラムを作り直してみよ。

□演習 10.5 (冪乗) 実数 a と 非負整数 k をパラメータとして受け取り ak を計算結果と して返す関数を定義せよ。

□演習 10.6 (Fibonacci数列) 一般に、初期値 a0, a1 と漸化式 an=an1+an2 for each n≥2

によって決まる数列 {an} をFibonacci数列という。46以下の非負整数 k をパラメータ として受け取り、初期値が a0 =a1 = 1 のFibonacci数列の第(k+ 1)項 ak を計算結果と して返す関数を定義せよ。

□演習 10.7 (三角形が出来るかどうかの判定) 3つの実数 a, b, c をパラメータとして受 け取り、a, b, cを3辺の長さとする三角形が存在するかどうかの判定結果を返す関数を定 義せよ。

ドキュメント内 新潟大学学術リポジトリ (ページ 144-148)