プログラミング実習

プログラミング論
第十五回
データの並べ替えをやってみよう
よく使う処理は関数にしよう
本日の内容


データの並べ替え
簡単な関数の例
–
–
–
–
–


関数の構成
ローカル変数の宣言
戻り値と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)。