プログラミング論 第十五回 データの並べ替えをやってみよう よく使う処理は関数にしよう 本日の内容 データの並べ替え 簡単な関数の例 – – – – – 関数の構成 ローカル変数の宣言 戻り値とreturn文 関数の呼び出し 関数のプロトタイプ宣言 戻り値のない関数 グローバル変数 データの並べ替えを考えてみよう まず,「データが大きい順に並んでいる」 という状態は,どういう状態かを考えまし ょう。 その状態を,「繰り返し」や「分岐」で簡潔 に表現できないか,を考えます。 次に,「その状態になっていなかったら, どのような処理をすればよいか?」を考え ましょう。 データの並べ替えを考えてみよう ~定義~ データがn個あるとき,「データが大きい順 に並んでいる」状態は次のように定義でき ます。 – [0]~[n-1]:[0]番目のデータが最も大きい – [1]~[n-1]:[1]番目のデータが最も大きい – [2]~[n-1]:[2]番目のデータが最も大きい ・・・ – [n-2]~[n-1]:[n-2]番目のデータが最も大きい データの並べ替えを考えてみよう ~繰り返しで表現できそうな場所は?~ – [0]~[n-1]:[0]番目のデータが最も大きい – [1]~[n-1]:[1]番目のデータが最も大きい – [2]~[n-1]:[2]番目のデータが最も大きい ・・・ – [n-2]~[n-1]:[n-2]番目のデータが最も大きい データの並べ替えを考えてみよう ~繰り返しで表現できそうな場所は?~ – [0]~[n-1]:[0]番目のデータが最も大きい – [1]~[n-1]:[1]番目のデータが最も大きい – [2]~[n-1]:[2]番目のデータが最も大きい ・・・ – [n-2]~[n-1]:[n-2]番目のデータが最も大きい 繰り返しでの表現? iを0~n-2まで変化させながら, [i]~[n-1]の中で[i]番目のデータが最も大きい がすべて成り立つならば,大きい順に並んでい る,と言える。 データの並べ替えを考えてみよう ~並んでいない場合(矛盾)の処理~ iを0~n-2まで変化させながら, [i]~[n-1]の中で[i]番目のデータが最も大きい がすべて成り立つならば,大きい順に並んでい る,と言える。 [i]番目が最も大きくない場合は, [i]~[n-1]で最大値を探しだし, その最大値と[i]番目のデータを交換する。 データの並べ替えを考えてみよう まずは,「データを交換するプログラム」を考 えてみましょう。 (sort1.c) 次に,並べ替えを行うプログラムを考えてみ ましょう。 (sort2.c) 他の方法を考えてみましょう。 関数の働き そもそも,「関数」って何? 何かが入力 されると・・ その入力に応じた 何かが出力される あるルールに従って このような関係にあるものを「関数」と呼びます。 一体,何が便利なの? – 同じ処理が何度も出てくる時は,一度「関数」として作っておけ ば,そこに入力するデータを変えるだけで結果を得られるので, プログラムが短くて済みます。 – 何度も同じ処理を書かなくて良いので,プログラムがスッキリ 見やすくなります。 関数の働き 例えば,こういう働きをするものは何? “Hello, world” printf( ) ですね。 つまり, printf( )も関数の一つなのです! このように,プログラムは,いろんな関数の集まりでで きています。 関数は,自分で作る事ができます! 関数の働き(まとめ) 関数とは,ある入力を与えると,ルールに従ってある結 果を返してくれるもの プログラムは関数の集まりでできており,言語が用意し ているもの(printfやscanf)だけでなく,自分自身で作る 事もできる 頻繁に繰り返される処理を関数として作っておくと,必 要な時に関数を呼び出し,入力するデータだけを変更 すれば良く,プログラムが短く分かり易くなる 関数ごとに役割を決め,関数の集まりとしてプログラム を作成することにより,誰が見ても分かり易く,使いや すくなる – データの受け渡し方法など約束を決める必要がある 「printfへ入力する文は,” “で囲め」,など 簡単な関数の例 では,関数を作ってみよう! 数学における関数を思い出しましょう。 関数 f に x を与えたときの結果を y に代入する これは,こう書きましたね。 y f (x) 実際に,関数 f が x をもらってどういう計算をしているかは,・・ f ( x) x 3x 2 2 と定義しましたね。 簡単な関数の例 では,関数を作ってみよう! プログラミング言語の場合も同じです。 f ( x) x 2 3x 2 y f (3) と関数を宣言して, のように利用します。 関数の宣言は,main関数の外で行います。 関数は,main関数など他の関数の中で利用されます。 自分自身の中で呼び出すこともできます(再帰処理) 簡単な関数の例 では,関数を作ってみよう! #include <stdio.h> double f( double x) { double y; int main(void) { double x,y; printf("x=?"); scanf("%lf", &x); y = x*x+3*x+2; y = f(x); return y; } printf("y=%f\n",y); } ここが関数の宣言部です 簡単な関数の例 では,関数を作ってみよう! #include <stdio.h> double f( double x) { 引数 double y; ローカル変数 y = x*x+3*x+2; return y; } 関数の処理は,{ }で囲んで 表します。 プログラム内では,変数は型 指定されなければなりません。 関数が呼び出されたところか ら受け取るデータを引数とい います。 関数の答えが何型になるの か,の指定も行います。 計算した後,関数の答えとし て出力する変数の値はreturn 文で返します。 関数の中で宣言される変数を ローカル変数と言います。関 数の中だけで有効です。 簡単な関数の例 では,関数を作ってみよう! 必ず,main関数から実行されます #include <stdio.h> int main(void) { double f( double x) 引数の値が double x,y; 渡されます。 { 両方ともロー カル変数の double y; printf("x=?"); ため,変数名 scanf("%lf", &x); は同じですが y = x*x+3*x+2; まったく別の 変数です。 渡された引数を元に y = f(x); 関数が呼び 処理を行います。 出されます return y; } printf("y=%f\n",y); 処理結果のyが関数の結果 として呼び出し元に返されます。 } 結果が画面に表示されます。 演習 ensyu44.cを完成させ,結果を確認しなさい。 引数として,直接数値を指定しても良いことを 確認しなさい。 ローカル変数名(引数と関数の結果の変数名) は,main関数と関数fとで異なっていても問題な いことを確認しなさい。 最後のprintfにて,変数yの値を出力する代わ りに,直接printf内にて関数fの呼び出しを行っ ても結果が出力されることを確認しなさい。 演習 実数を四捨五入して整数にする処理を, 関数で実現するプログラムを作成しなさ い。 ensyu45.cを完成させなさい。 プロトタイプ宣言 ensyu46.cは,ensyu44.cを改訂したもの です。 main関数を最初に配置し,後ろに関数f を配置しています。 コンパイルするとどうなるでしょうか? プロトタイプ宣言 こんなエラーが出ます。 警告 W8065 ensyu46.c 12: プロトタイプ宣言のない 関数 'f' の呼び出し(関数 main ) エラー E2356 ensyu46.c 20: 'f' の再宣言で型が一致していない では,一つずつ考えていきましょう。 プロトタイプ宣言 警告 W8065 ensyu46.c 12: プロトタイプ宣言のない 関数 'f' の呼び出し(関数 main ) #include <stdio.h> int main(void) { double x, y; printf("x="); scanf("%lf",&x); y = f(x); printf("y=%f\n",y); } double f(double x) { double y; y = x*x+3*x+2; return y; } ここの段階でこのメッセージ が出力されます。 ここでいきなり初めて関数fと いうのが出てきた。 こんなものは聞いたことがない から勝手に解釈して先に進み ます。 プロトタイプ宣言 エラー E2356 ensyu46.c 20: 'f' の再宣言で型が一致していない #include <stdio.h> int main(void) { double x, y; printf("x="); scanf("%lf",&x); y = f(x); printf("y=%f\n",y); } double f(double x) { double y; y = x*x+3*x+2; return y; } ここの段階でこのメッセージ が出力されます。 さっき初めて見たからこっちで 勝手に解釈していたのに, こんなところで教えてくれたって もう遅い! プロトタイプ宣言 というわけで,初めて作って利用する関数は, 「呼び出す関数の前に宣言をすませておかな いと,コンパイル時に解釈できなくてエラーが出 る」ということになります。 プリプロセッサの位置に,「これからこういう関 数を使いますよ」という関数の形の宣言をしま す。これを「プロトタイプ宣言」といいます。 関数名,引数の数・型,返り値(関数の結果)の 型を宣言します。 ensyu47.cでプロトタイプ宣言の方法を確認し て下さい。 プロトタイプ宣言 #include <stdio.h> double f(double); int main(void) { double x, y; printf("x="); scanf("%lf",&x); y = f(x); printf("y=%f\n",y); } double f(double x) { double y; y = x*x+3*x+2; return y; } プロトタイプ宣言 double f(double); 引数の型と数 関数名 関数の結果(返り値)の型 printfやscanfは,標準ライブラリ 関数と言われ,stdio.hの中に記載 してあるため,エラーとならない。 返り値のない関数 返り値がない関数の場合は,関数宣言時の返 り値の型はvoidで宣言をします (ensyu48.c) #include <stdio.h> void putd(int); int main(void) { int dt=100; putd(dt); return 0; } void putd(int a) { printf("%d\n",a); } 関数の型宣言 はvoid型となる 関数が値を返さない =return文がない 演習:よく間違う例 ensyu49.cは,変数xと変数yの値を交換 するプログラムとして作ってみたものです。 実行して,変数値が交換できることを確 認しなさい。 ensyu50.cは,交換の機能をswapという 関数で実現しようとしたものです。しかし, これでは変数値が交換されません。この 理由を考えなさい。 グローバル変数 関数内で宣言された変数はローカル変数と呼 ばれ,その関数内でのみ有効です。 同じ名前の変数があったとしても,異なる関数 内で宣言されていれば,「違う家に住んでいる 同姓同名の人」という扱いになり,まったく別の 変数,と言うことになります。 ただし,関数の外,プリプロセッサの位置で宣 言された変数は「グローバル変数」と呼ばれ, どの関数からでも共通して利用できる変数にな ります。 ensyu51.cで確認しましょう。 グローバル変数 #include <stdio.h> double r; グローバル変数 double pi=3.141592; double menseki(void); double ensyu(void); この値を共通 int main(void) に利用できる { double c, s; printf("r="); scanf("%lf",&r); c=ensyu(); s=menseki(); printf("ensyu=%f, menseki=%f\n",c,s); return 0; } double ensyu(void) { return 2*r*pi; } double menseki(void) { return r*r*pi; } 共通に利用できる 演習 int型引数一つを受け取り,その数値の階 乗を計算する関数int kaijo(int n)を完成 させなさい(ensyu52.c)。 グローバル変数を用いることによりデータ 交換を可能にした関数void swap(void) を完成させなさい(ensyu53.c)。
© Copyright 2024 ExpyDoc