計算機プログラミング
第5 回 流れの制御 2
担当:知能ソフトウェア研究室
今日の内容
講義の内容 I while 文、for 文(繰り返し) 演習の内容 I 第5 回の問題セット I 問題1(必修) I 問題2(発展)同じ処理を特定の回数繰り返す
例題:アスタリスク * を40 文字出力する 解法1 :「アスタリスク40 文字分」の文字列を手作業で作成する方法 printf (" ** ** * ** * * * * * * * * * ** ** * * * * * * ** * ** * ** ** * ** * \ n "); 解法2 :反復処理による、文字数の変化に対応しやすい方法 int aster_num ; /* 出 力 す る 文 字 数 ( 目 標 文 字 数 ) */ int aster_count ; /* 出 力 済 み の 文 字 数 */ aster_num = 40; aster_count = 0; /* 目 標 文 字 数 に 達 す る ま で 、 1 文 字 の 出 力 を 繰 り 返 す */while( aster_count < aster_num ){
printf (" * ");
aster_count = aster_count + 1; // 変 数 の 値 を 1 増 や す
}
ある処理を、変数の値を変えながら繰り返す
I 問題:摂氏(セルシウス度)の温度 C を華氏(ファーレンハイト度)F に 以下の関係式に従い変換する。 F = 9 5C + 32 I 入力:温度を表す実数(単位は摂氏) I 出力:入力された温度を華氏に変換して得られる実数 double celsius ; /* 入 力 さ れ た 温 度 ( 摂 氏 ) */ double fahrenheit ; /* 出 力 す る 温 度 ( 華 氏 ) */ scanf (" % lf ", & celsius );fahrenheit = 9* celsius /5 + 32; /* 単 位 変 換 */ printf (" %6.2 lf ␣ C ␣ = ␣ ", celsius ); printf (" %6.2 lf ␣ F \ n ", fahrenheit ); 出力結果(1 行目は入力) 12.8 12.80 C = 55.04 F
ある処理を、変数の値を変えながら繰り返す
I 問題:色々な摂氏温度について、対応する華氏温度を出力する double celsius ; double fahrenheit ; double begin_celsius = 8; /* 摂 氏 温 度 の 初 期 値 ( 区 間 の 下 端 ) */ double end_celsius = 20; /* 摂 氏 温 度 の 上 限 ( 区 間 の 上 端 ) */ double step_celsius = 0.4; /* 摂 氏 温 度 の 刻 み 幅 */ celsius = begin_celsius ;while( celsius <= end_celsius ){ fahrenheit = 9* celsius /5 + 32; printf (" %6.2 lf ␣ C ␣ = ␣ ", celsius ); printf (" %6.2 lf ␣ F \ n ", fahrenheit ); celsius = celsius + step_celsius ; } 出力結果 8.00 C = 46.40 F 8.40 C = 47.12 F 8.80 C = 47.84 F 9.20 C = 48.56 F ( 途 中 省 略 ) 19.60 C = 67.28 F 20.00 C = 68.00 F
while
文:処理の繰り返し(反復、ループ)
I 書式: while(条件式){ 文の並び } I 条件式が真である間、文の並びを繰り返し実行する I 「条件式が真であれば文の並びを実行する」点はif文と同じだが、真である限 り何度でも繰り返される点が異なる I 文の並びの実行途中で条件式が偽になったとしても、文の並びは最後まで実行 される 例:1 から 10 までの整数を出力する int i ; i = 1; while( i <= 10 ){ printf (" % d ␣ ", i ); i = i + 1; } 出力結果 1 2 3 4 5 6 7 8 9 10反復処理の多重化の例:九九の表の出力
出力: 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 ( 途 中 省 略 ) 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 重要:複雑な(反復)処理は、段階的に詳細化し、徐々に簡単な処理に分解する 段階1 /* 1 の 段 か ら 9 の 段 ま で 、 順 に 出 力 す る */ 段階2 row = 1; while( row <= 9 ){ /* row の 段 を 出 力 す る */row = row + 1; /* row を 次 の 段 の 値 に 更 新 す る */ } 段階3 row = 1; while( row <= 9 ){ col = 1; while( col <= 9 ){ /* row の 段 の col 列 目 の 値 を 出 力 す る */ col = col + 1; }
printf (" \ n "); row = row + 1; }
反復処理の多重化の例:九九の表の出力
int row ; int col ; int value ; row = 1;
while( row <= 9 ){ /* row の 段 を 出 力 す る */ col = 1;
while( col <= 9 ){ /* col 列 目 の 値 を 出 力 す る */ value = row * col ; /* row の 段 の col 列 目 の 値 */ printf (" %3 d ␣ ", value ); /* %3 d : 3 文 字 分 の 幅 で 出 力 */ col = col + 1; /* col を 次 の 列 の 値 に 更 新 す る */ }
printf (" \ n "); /* 1 段 出 力 し 終 え た の で 、 改 行 す る */ row = row + 1; /* row を 次 の 段 の 値 に 更 新 す る */ } 出力結果 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 ( 途 中 省 略 ) 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81
典型的な反復処理:様々な処理の結果を累積する
accum = ...; /* 累 積 変 数 の 初 期 化 */
current = ...; /* 反 復 処 理 の 進 度 を 表 す 変 数 の 初 期 化 */
while( current に つ い て の 条 件 ){
accum = /* accum と current を 使 っ た 式 */ current = /* current を 使 っ た 式 */ } 例:1 から 100 までの整数の総和を求める int sum ; /* 和 を 累 積 す る 変 数 ( 1+2+...+( k−1) ) */ int k ; /* 和 を ど こ ま で 求 め た か (k −1ま で 求 め た ) */ sum = 0; k = 1; while( k <= 100 ){ /* k != 101 で も よ い */ sum = sum + k ; k = k + 1; } printf (" % d \ n ", sum ); 出力結果 5050
典型的な反復処理:条件を満たす解の探索
found = 0; /* 「 解 が 見 つ か っ た 」 こ と を 表 す 真 偽 値 の 初 期 化 */ x = ...; /* 解 の 候 補 の 初 期 化 */while( ! found && x に つ い て の 条 件 ){
if( x が 解 で あ る ){ answer = x ; found = 1; } x = /* 次 の 解 の 候 補 */ } if( found ){ /* answer を 使 っ た 、 解 が 見 つ か っ た 場 合 の 処 理 */ } 例:与えられた数n の、2 以上 n 未満の最小の約数を探す scanf (" % d ", & n ); found = 0; d = 2;
while( ! found && d < n ){
if( n % d == 0 ){ divisor = d ; found = 1; } d = d + 1; }
典型的な反復処理:累積と探索の組み合わせ
例:与えられた数の約数の数を求める scanf (" % d ", & n ); num_divisor = 0; d = 2; while( d < n ){ if( n % d == 0 ){ num_divisor = num_divisor + 1; } d = d + 1; } printf (" % d \ n ", num_divisor );for
文:典型的な
while
文を簡潔に書ける構文
I 書式: for(初期化文;条件式;更新文){ 文の並び } I while 文を使った、以下の一連の文と同じ処理を行う 初期化文; while(条件式){ 文の並び; 更新文; } I 「条件が成り立つ間、変数を更新しつつ処理を繰り返す」場合に適している I 今日の例題は全てこのような処理となっている I 使い慣れると便利だが、最初は無理に使わずwhile文で書くことを推奨 例:1 から 100 までの総和を求める sum = 0; for( k = 1; k <= 100; k = k + 1){ sum = sum + k ; } 例:九九の表を出力するfor( row = 1; row <= 9; row = row + 1){
for( col = 1; col <= 9; col = col + 1 ){ value = row * col ;
printf (" %3 d ␣ ", value ); }
printf (" \ n "); }
第
5
回の課題
問題1(必修) 以下の問題を解くプログラムを投稿する。 I 問題:与えられた数が完全数であるかどうかを判定する。完全数とはその数 の約数の総和がその数自身と等しい正の整数のことであり、ここで約数に はその数自身は含めないものとする。例として、6 の約数は 1, 2, 3 であり、 1 + 2 + 3 = 6であるから 6 は完全数である。 I 入力:正の整数n I 出力:n が完全数であれば n is a perfect number. と、そうでなければn is NOT a perfect number. を標準出力に出力する。
実行例1(最初の行は入力)
4
4 is NOT a perfect number .
実行例2(最初の行は入力)
28
28 is a perfect number .
実行例3(最初の行は入力)
8127
8127 is NOT a perfect number .
実行例4(最初の行は入力)
33550336
第
5
回の課題
問題2(発展) 以下の問題を解くプログラムを投稿する。 I 問題:自然対数の底 e の近似値 e0 を以下の式を用いて求める。 e0= 10 X n=0 1 n! ここで n! は n の階乗であり、0! = 1 とする。なお 10! は約360 万であるた め、int 型の範囲に収まると考えてよい。 I 入力:なし I 出力:上記の実数 e0 を、標準出力に書式 %20.18lf で出力する。 実行例 2.718281801146384500反復処理についての注意点
I while 文の書き方によっては、プログラムがずっと実行し続けて止まらない 無限ループに陥ることがある I 実行中のプログラムは、端末上で「Ctrl-C」(Ctrl キーを押しながら C キー)を押すと強制的に終了させることができる I 無限ループに陥っているように見える場合は、強制終了させる I ある程度時間が経過しても、何も出力が得られない時など I 無限ループの原因は概ね「条件式の間違い」か「反復の進度を表す変数が適 切に更新されていない」ことのいずれか I 反復の進度を表す変数の内容を(反復中に)出力し、想定通りの内容か確認する課題の投稿についての注意点
I 問題に併記しているすべての実行例について、正しく動作することを確認 する I 正しい結果が得られない実行例が1つでもある場合は、大体不合格になる I 問題によっては、併記した実行例以外の例を採点時に用いていることがある。 このため、併記した全ての実行例について正しく動作したからといって合格す るとは限らない I 現実の問題では、実行例が与えられることは少なく、自分で実行例(ある入力 に対する正しい出力は何か)を考える必要があることに注意 I 出力する(文字列の)内容は正確に I 空白、ピリオドなどの記号を含めた、計算結果以外の内容 I 数を出力する場合は、整数と実数のどちらを選ぶか I 指定された内容以外のものを出力しない I データを標準入力から入力させる場合、「...を入力してください」といったガ イドを出力すること自体は良いことだが、課題として提出する際はその出力は 行わないようにする(コメントアウトしてから出す)適切なインデントによる、構造の明瞭化
インデントされていない例 if( type == 0){ if( alc > 37 ){ tax = ...;} else{ tax = ...;}} else{if( alc <= 20 ){ tax = ...;} else{ tax = ..;}} printf (...); インデントされている例 if( type == 0){ if( alc > 37 ){ tax = ...; } else{ tax = ...; } } else{ if( alc <= 20 ){ tax = ...; } else{ tax = ...; } } printf (...); 1. 文の終わり ; と波括弧 { } の後に改行を入れる 2. 全体を選択し、TAB キーを押す(選択範囲を自動インデント)ソースコード作成中の、積極的なインデント
I 特に何も選択せずにTAB キーを押すと、現在の行(カーソルがある行)が 適切にインデントされる I セミコロン ; 、丸括弧 ( 、改行文字などの特別な文字が入力された時も 現在の行がインデントされる I このため、インデントはあまり積極的に行わなくてもある程度行われる形 になるが、基本的には「新しく行を書き始めるときはまず最初にTAB キー を押し、それから書き始める」のがよい I 既に書いた行の一部を後で修正した場合も、それによってインデントが崩 れることがあるので、その場合は再度TAB キーを押してインデントし直す I 既に書いた部分を、後からブロック { } の中身にする場合は、その中身は 自動的にインデントされないので、手動でインデントし直すようにする I ブロックの中身の部分(を含むような広い範囲)を選択し、TABキーを押す if( type == 0 ){ /* 後 か ら 追 加 */ area = l * l ; volume = area * h ; } /* 後 か ら 追 加 */ 中央2 行を 選択しTAB if( type == 0 ){ area = l * l ; volume = area * h ; }コンパイル時のエラーメッセージ
I 問題のあるソースコードをgcc でコンパイルすると、エラーメッセージが出 力される I エラーメッセージが出力された場合は実行ファイルも生成されていないの で、問題の原因を見つけて修正する必要がある I エラーメッセージは、大抵の場合問題が生じた行番号および問題の内容を 含んでいるので、それを手掛かりに原因を考える I その行番号の行そのものには問題がなく、別の場所に問題があることも 例:文末のセミコロンが無い(5 行目の文の実行に支障が出ているので、その前の行を見直す)hello . c :5: error : expected ‘; ’ before ‘p r i n t f ’
例:変数名の間違い(定義されていない変数の利用)
hello . c :7: error : ‘a r a ’ undeclared ( first use in this function )
例:関数名の間違い(定義されていない関数の呼び出し)
hello . c :(. text +0 x11 ): undefined reference to ` print'
例:include 文で指定するファイル名の間違い