自分の科目の中で,学んでほしい…必ずしも書けなくてもいいけど,コードが与えられれば,コンパイル・実行することなく,どんな出力になるかが分かるようになってほしい…反復処理を,洗い出してみました.
- 単純なwhile文
- 単純なfor文
- 単純なdo〜while文
- デクリメントのfor文
- 等差数列で値が変わるfor文
- 倍々/半々に値が変わるfor文
- 独立型2重ループ
- 従属型2重ループ
- フィルタ入り2重ループ
- 間に処理の入る2重ループ
- 2変数同時ループ
- 3重ループ
- ループの中で関数呼び出し
- ループの中で関数(ループあり)呼び出し
- 再帰呼び出しによるデクリメント
- 再帰呼び出しによるインクリメント
検証コードです.出力は各自どうぞ.
#include <stdio.h> /* 単純なwhile文 */ void simple_while(void) { int i; i = 1; while (i <= 3) { printf("i = %d\n", i); i++; } } /* 単純なfor文 */ void simple_for(void) { int i; for (i = 1; i <= 3; i++) { printf("i = %d\n", i); } } /* 単純なdo〜while文 */ void simple_do_while(void) { int i; i = 1; do { printf("i = %d\n", i); i++; } while (i <= 3); } /* デクリメントのfor文 */ void for_decrement(void) { int i; for (i = 3; i > 0; i--) { printf("i = %d\n", i); } } /* 等差数列で値が変わるfor文 */ void for_arithmetical(void) { int i; for (i = 2; i <= 8; i += 3) { printf("i = %d\n", i); } } /* 倍々に値が変わるfor文 (for_double_and_halfの下請け) */ void for_double(void) { int i; for (i = 1; i <= 4; i <<= 1) { printf("i = %d\n", i); } } /* 半々に値が変わるfor文 (for_double_and_halfの下請け) */ void for_half(void) { int i; for (i = 4; i > 0; i >>= 1) { printf("i = %d\n", i); } } /* 倍々/半々に値が変わるfor文 */ void for_double_and_half(void) { printf(" <<for_double>>\n"); for_double(); printf(" <<for_half>>\n"); for_half(); } /* 独立型2重ループ */ void twofold_for_independent(void) { int i, j; for (i = 1; i <= 3; i++) { for (j = 1; j <= 3; j++) { printf("i = %d, j = %d\n", i, j); } } } /* 従属型2重ループ */ void twofold_for_dependent(void) { int i, j; for (i = 1; i <= 3; i++) { for (j = 1; j <= i; j++) { printf("i = %d, j = %d\n", i, j); } } } /* フィルタ入り2重ループ */ void twofold_for_filtered(void) { int i, j; for (i = 1; i <= 3; i++) { for (j = 1; j <= 3; j++) { if (i >= j) { printf("i = %d, j = %d\n", i, j); } } } } /* 間に処理の入る2重ループ */ void twofold_for_intricate(void) { int i, j; for (i = 1; i <= 3; i++) { printf("i = %d: ", i); for (j = 1; j <= 3; j++) { printf("j = %d, ", j); } printf("\n"); } } /* 2変数同時ループ */ void for_synchro(void) { int i, j; for (i = 1, j = 3; i <= 3; i++, j--) { printf("i = %d, j = %d\n", i, j); } } /* 3重ループ(行列の積) */ void threefold_for(void) { int i, j, k; for (i = 1; i <= 2; i++) { for (j = 1; j <= 2; j++) { printf("c[%d][%d] = ", i, j); for (k = 1; k <= 2; k++) { printf("a[%d][%d] * b[%d][%d]", i, k, k, j); if (k < 2) { printf(" + "); } else { printf("\n"); } } } } } /* iの値を出力 (for_function_callの下請け) */ void print_i(int i) { printf("i = %d\n", i); } /* ループの中で関数呼び出し */ void for_function_call(void) { int i; for (i = 1; i <= 3; i++) { print_i(i); } } /* iとの値を出力 (for_function_call2の下請け) */ void print_ij(int i) { int j; for (j = 1; j <= 3; j++) { printf("i = %d, j = %d\n", i, j); } } /* ループの中で関数(ループあり)呼び出し */ void for_function_call2(void) { int i; for (i = 1; i <= 3; i++) { print_ij(i); } } /* 再帰呼び出しによるデクリメント(カウントダウン) */ void recursive_decrement(int i) { printf("i = %d\n", i); if (i > 1) { recursive_decrement(i - 1); } } /* 再帰呼び出しによるインクリメント(カウントアップ) */ void recursive_increment(int i) { if (i > 1) { recursive_increment(i - 1); } printf("i = %d\n", i); } int main(void) { printf("<<simple_while>>\n"); simple_while(); printf("<<simple_for>>\n"); simple_for(); printf("<<simple_do_while>>\n"); simple_do_while(); printf("<<for_decrement>>\n"); for_decrement(); printf("<<for_arithmetical>>\n"); for_arithmetical(); printf("<<for_double_and_half>>\n"); for_double_and_half(); printf("<<twofold_for_independent>>\n"); twofold_for_independent(); printf("<<twofold_for_dependent>>\n"); twofold_for_dependent(); printf("<<twofold_for_filtered>>\n"); twofold_for_filtered(); printf("<<twofold_for_intricate>>\n"); twofold_for_intricate(); printf("<<for_synchro>>\n"); for_synchro(); printf("<<threefold_for>>\n"); threefold_for(); printf("<<for_function_call>>\n"); for_function_call(); printf("<<for_function_call2>>\n"); for_function_call2(); printf("<<recursive_decrement>>\n"); recursive_decrement(3); printf("<<recursive_increment>>\n"); recursive_increment(3); return 0; }
少し補足を.
- 実用としては,printfでiやjの値を出力しているところを,iやjの値に応じた何らかの処理に置き換えることになります.もちろん処理範囲も変わります.配列処理においては,通常,初期値を0とし,反復条件は(「<=」ではなく)「ループカウンタ < 配列の要素数」という書き方になります.
- simple_while, simple_for, simple_do_whileでそれぞれの反復を終えるときのiの値は,3ではなく4です.i=4となっているから,「i <= 3」が偽となるわけです.
- for_doubleの「i <<= 1」は,「今のiの値を左に1ビットシフトし,それを新しいiの値にしなさい」の意味です.2進数で考えると,値を倍にするということです.for_halfの「i >>= 1」はその逆で,値を半分にするのですが,1という値を右に1ビットシフトすると,0になります.
- twofold_for_dependentには,従属型(内部ループの値を取り得る範囲が,外部ループの値に依存する)2重ループの中で最も単純なものを記述しました.従属型2重ループのバリエーションは,nlmateで学んでください.
- twofold_for_independet, twofold_for_dependent, twofold_for_filteredでは,関数内部の(forまたはifに関する)「{」と「}」の文字は除去可能ですが,twofold_for_intricateでは,jのforに関する「{」と「}」のみが除去可能です.
- 独立型2重ループと2変数同時ループの違いは,意外と重要かもしれません.前者は出力(printf関数の呼び出し)を合計9回行いますが,後者は3回です.
- for_function_call2とprint_ijを学ぶ価値は何かというと,それぞれは単ループですが,ループの中で関数を呼び出すことによって,2重ループを作ることができるという点です.
- もう少し高度な反復として,「単ループの値を分解*1して2重ループ扱い」「2重ループだけど単ループ扱い」「breakを含むループ」「continueを含むループ」があります.1年生の段階で学んでも決して悪くありませんが,検証コードが複雑になりがちなので,今回は割愛しました.
*1:例えば整数値を3で割った商と余りに分解するとか,上位ビットと下位ビットに分解するとか.