Comments
Description
Transcript
第3回テキスト
プログラミング演習 第3回 今日の主なテーマ 関数 再帰関数 変数の記憶クラス 条件判定と繰り返しの総合練習問題 1. 1から100までの整数を表示するプログラムを、do-while 文、while文、for文それぞれで作成せよ。 2. キーボードから整数の入力を何度か受け付け、それら の合計が100以上になったら終了するプログラムを作成 せよ。どの繰り返し構文を使うのが適切か? 3. 任意の整数入力nに対し、コラッツ予想を確認してみよ – nが偶数のときは2で割る – nが奇数のときは3倍し、1を足す 以上の操作を繰り返せば必ず1に到達するというもの 2 2からNまでの素数を列挙せよ • 2つのアルゴリズムを試してみよ。 1. 1つのnが素数であるかどうかを判定し表示するアルゴリ ズムを考え、それをnを2からNまで巡回する繰り返し構 文の中に入れる(練習問題4.10 - 4.11)。 2. エラトステネスの篩を用いる。 1. 2. 3. 4. 5. 2からNまでの探索リストを用意する 探索リストの先頭を素数リストに追加する ステップ2で追加した数の倍数を探索リストから削除する 探索リストの最大値がステップ2で追加した数の平方よりも大き ければステップ2に戻る。 素数リストと探索リストに残った数が全て素数である C言語では可変長リストを扱うのが難しいので、要素数 Nのchar型配列を用い、その値が0か1で値が存在する か否かを表すことにして可変長リストとみなそう。 3 配列の練習問題 • N要素の整数配列を作成し、初期値として適当な値を入 れておく。 1. 全要素を順番に表示せよ。 2. 合計・最大・最小値・平均値を求めよ。 3. 小さい順に表示せよ。 4. 配列の中身が小さい順になるように並べ替えよ。 4 先週のおさらい(1/2) • プログラムの流れを先へ進める – if文, if~else文, if~else if~else文 条件式に応じた分岐処理を実行 if((a%b)==0){ printf("%d is a factor", b); } – switch~case文 ある式が取り得る値に応じて先に進む switch(c){ case 'A': printf("Great¥n"); case 'B': case 'C': printf("You did it¥n"); break; default: printf("You lost¥n"); } 5 先週のおさらい(2/2) • プログラムの流れを繰り返す – while文,do~while文, for文 ある条件が成立する間、同じ処理を繰り返し実行 while(i>1){ i/=2; } do{ i/=2; } while(i>1); for(i=0; i<10; i++){ j+=i; } 6 関数の基本事項(1/4 p.36) • C言語によるプログラムはmain()関数を発端とした関数 の集まり。 • プログラム中に定義(宣言)された関数は他の関数から 呼び出される。 • 関数が呼ばれるとデータの引渡しが行われ、引き渡され たデータに基づいて演算が実行される。 • 演算により得られた結果は関数値として呼び出された関 数へ返される。この返される値を戻り値(返戻値)と呼ぶ。 戻り値は1つだけ指定できる。 • 戻り値を呼び出された関数へ渡す時にreturn文が使 われる。戻り値となる変数の型がその関数の型となる。 関数の宣言時には戻り値の型に応じて型宣言しなけれ ばならない。 7 関数の定義 (2/4) • 関数を定義するには、以下の項目を( )と{ }で区切りなが ら書く 関 数 の 型 – – – – 戻り値の型 関数名 受け取る引数の個数と型 関数の実体(処理内容) int name(char c){...} • 関数は、全て他の関数の外で定義する – 関数の中では別の関数を定義できない • 関数は、呼び出されない限り何もしない – 実行するには直接あるいは間接的にmain関数から呼び出す • 引数を受け取らない場合や戻り値を戻さない場合には、 特別な型voidを用いて明示する 8 関数の基本事項(3/4 p.37) 戻り値の 型が一致 #include <iostream> using namespace std; 仮引数 int sum(int x, int y){ int z; z=x+y; return z; 仮引数と 引数が存在しない場合 } 実引数の int main(void){ 個数と型が int i, j, k; cout << “Input two integers: ”; 一致 cin >> i >> j; cout << “The sum of these two is: ”; k=sum(i, j); cout << k << endl; return 0; 実引数 } 9 関数の基本事項(4/4) • 関数を呼び出す時点で関数の型が分かっていれば、 引数や戻り値の型を自動的に型変換してくれる。 double sum(double x, double y){ return x+y; } int main(void){ int i, j, k; ... k=sum(i, j); // 自動的に // k=(int)sum((double)x, (double)y); // という型変換が行われる ... return 0; } 10 プロトタイプ宣言(1/2 p.37) • 先に関数の型が分かっていないと、型変換せずにデータ の引渡しを行うことになり、計算結果に致命的な誤りを生 じる。 int main(void){ int i, j, k; k=sum(i, j); return 0; } double sum(double x, double y){ return x+y; } • 常に呼び出される関数を先に記述することは不可能なの で、関数型だけを先に宣言する方法が用意されている。 これをプロトタイプ宣言という。 double sum(double, double a); 仮引数の名前はあってもなくても良い 11 プロトタイプ宣言(2/2 p.37) #include <iostream> double sum(double, double); /* プロトタイプ宣言 */ int main(void){ int i,j,k; cout << “Input two integers: ”; cin >> i >> j; cout << “The sum of these two is: ”; k=sum(i,j); cout << k << endl; return 0; } double sum(double x, double y){ int z; z=x+y; return z; } 引数の型や個数、関数値の型 を検証するため、関数の定義 に先だって型宣言を行う 12 引数の受け渡し(1/2 p.38) 関数間での変数の引渡し •pass by value(値呼び出し) •値のコピーを渡す •例: printf(“%d\n”, a); •pass by reference(参照値呼び出し) •参照を渡す •C言語でのpass by reference •アドレスのコピーを渡す •例: scanf(“%d\n”, &a); 13 引数の受け渡し(2/2 p.38) #include <iostream> using namespace std; void func(int x, int &y, int *z){ ++x; ++y; ++*z; cout << "x=" << x << ", "; cout << "y=" << y << ", "; cout << "z=" << *z << "¥n"; } int main(void){ int a=10, b=20, c=30; func(a, b, &c); cout << "a=" << a << ", "; cout << "b=" << b << ", "; cout << "c=" << c << "¥n"; return 0; } 引数をポインタで受け取ると、 コード中に&や*を付けなけ ればならない 引数を参照で受け取ると、 厄介な*を付けずに済む 14 デフォルト引数 (p.39) •C++言語では、引数のデフォルト値を与えることができる •デフォルト値がある引数は、呼び出し時に省略できる #include <iostream> void line(int n, char c='-'){ for(int i=0; i<n; i++){ cout << c; } cout << endl; } main(void){ line(10, '='); line(10); // 第二引数を省略している return 0; } 15 多重定義 (p.39) •C++言語の関数には、引数並びさえ異なれば同じ名前を 命名しても良い #include <iostream> void print(long x){ cout << "long value: " << x << endl; } void print(double x){ cout << "double value: " << x << endl; } int main(void){ long l=123; double d=1.23; print(l); print(d); return 0; } 16 再帰関数 (p.40) •C言語の関数は、直接的・間接的に自分自身を呼び出す ことが可能である。このような呼び出し方のことを再帰呼び 出しと言い、そのような関数を再帰関数と呼ぶ。 #include <iostream> int fact(int); int main(void){ int i=10; cout << fact(i); return 0; } int fact(int n){ if(n==1) return 1; else return(n*fact(n-1)); } 1 (n 1) n! n (n 1)! (n 1) 17 名前空間 (1/2 p.40-41) • C++言語は、複数の名前空間を持つ。 • 大域的な名前空間 • 名前付きの名前空間 namespace test { • 名前なしの名前空間 void func(void); (無名名前空間) void sub(void); • 名前空間に閉じ込められた物は、 } スコープ演算子を用いて指し示す void test::func(void){ ことができる sub(); } • 名前空間名 :: 対象名 void test::sub(void){ } int main(void){ test::func(); return 0; } 18 名前空間 (2/2 p. 41) • using宣言 – ある名前空間中の一部の名前に関し、名前空間名とスコープ演 算子を省略して表記することを許可する – 例:using std::cout; cout << "Hello" << std::endl; • using指令 – ある名前空間中の全ての名前に関し、名前空間名とスコープ演 算子を省略して表記することを許可する – 例:using namespace std; cout << "Hello" << endl; 19 自習、復習 • ここまでに登場してきたサンプルプログラムを入力し、 動作を確認してみよう。 • デバッガを利用してステップ実行することにより、関数呼 び出しの動作の様子を把握しよう。 • 関数宣言の順序、プロトタイプ宣言の必要性を理解しよう。 • 練習問題5.1~5.4 20 変数の有効範囲と生存期間(1/5 p.41-44) • 関数毎に同じ名前の変数を独立して使いたい(局所性) – 他の関数の中のことは知らずに済ませたい • 同じ変数を複数の関数で共有したい(大域性) • このように相反する要求を満たすため、記憶域クラスとい う概念が導入されている。変数は、同じ名前を使うことが 頻繁であるから、局所的であることが基本となっている。 • 同様に、関数にもクラスという概念がある。関数は大域的 であることが基本であるが、他のファイルからは呼び出 せない局所関数を定義することができる。 • C++言語では、大域的なものを名前空間に閉じ込めるこ とができる 21 変数の記憶クラス(2/5 p.41-44) •自動変数:autoによって定義される変数である。クラス指定を 省略するとautoとみなされる。有効範囲は変数が定義されたブ ロック内である。ブロックの開始と同時に領域が確保され、ブ ロックの終了と同時に消滅する。(テキストp.42参照) •静的変数:変数の有効範囲は自動変数と同じであるが、プロ グラムの開始と同時に領域が確保され、プログラムの終了まで 存在し続ける。初期値のある場合は1回だけ初期化され、関数 が終わっても変数の値は保持される。 •外部変数:関数の外部で定義された変数を外部変数と呼ぶ。 メモリの記憶領域、寿命に関しては静的変数と同じ。外部変数 はexternを利用すると、異なるファイル間での参照が可能とな る。それに対し、自動変数、静的変数は内部変数と呼ばれ、定 義された関数(ブロック)の内部でのみ有効である。 22 自動変数と静的変数の違い(3/5 p.42-43) #include <iostream> void test(void){ int a=0; static int s=0; cout << "a=" << a << " s=" << s << endl; a++; 同じ関数を3回呼び出しているが s++; aは同じ値、sは異なる値が } 表示されることに注意。 int main(void){ test(); a=0 s=0 test(); a=0 s=1 a=0 s=2 test(); return 0; } 23 外部変数のautoとstaticの違い(4/5 p. 43) // source1.cpp static int abc=5; extern int xyz; int func1(void){ cout << "In func1, "; cout << "abc=" << abc; cout << ", xyz=" << xyz << endl; return abc; } 実行結果予想 In func1, abc=5, xyz=1 In func2, xyz=5 // source2.cpp int xyz=1; int func1(void); void func2(void){ cout << "In func2, "; cout << "xyz=" << xyz << endl; } int main(void){ xyz=func1(); func2(); return 0; } 変数の記憶クラス(5/5 p.44) 宣言場所 クラス指定 領域確保と 生存時間 同一ファイル内で 宣言したブロック外 から参照が可能か 他のファイルから 参照が可能か 関数内、ブロック内 関数外 指定なし, auto, static static 指定なし register ブロックに同期 プログラムに同期 不可能 不可能 可能 可能 25 自習、復習 • テキストのソースコード9~16を入力し、それぞれの意図 する事柄を確認せよ • 練習問題5.5-5.14 26 第4回目の演習問題 1. a, b, c, hの値をキーボードから入力し、練習問題 4.13の矩形則を用いた数値積分を用いて 1 s (ax bx c)dx 2 0 を求めよ。hの値を変えた時の値を記録して、解析解と の誤差とhの関係を調べてみよ。 2. 問題1.を s=integral(a,b,c,h) として利用でき る関数として定義せよ。main関数から繰り返し呼び出 すことで、hとsの関係を表示してみよ。 例:h=0.1, s=1.2 h=0.01, s=1.25 h=0.001, s=1.248 27 数値積分(p.35) • 解析的積分が不可能な関数を積分する手法 • 刻み幅 h に依存して計算精度や計算時間が変わる – h を小さくし過ぎても、誤差が生じて計算精度が落ちる 場合があることに注意 b a a b f ( x)dx f ( x) h xa h b 28 算術関数を利用するには • 算術関数は、標準では使われない算術ライブラリ中に 用意されているので、算術関数定義ファイル cmath をインクルードすることを忘れてはならない。 #include <cmath> • 知っていて便利な算術関数 三角関数 sin(x), cos(x), tan(x) 指数、対数 exp(x), log(x), log10(x) 冪乗、根 pow(x,y), sqrt(x) 端数処理、絶対値 ceil(), floor(), fabs() 29 4 1 3 2 5