わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

反復処理いろいろ

自分の科目の中で,学んでほしい…必ずしも書けなくてもいいけど,コードが与えられれば,コンパイル・実行することなく,どんな出力になるかが分かるようになってほしい…反復処理を,洗い出してみました.

  1. 単純なwhile文
  2. 単純なfor文
  3. 単純なdo〜while文
  4. デクリメントのfor文
  5. 等差数列で値が変わるfor文
  6. 倍々/半々に値が変わるfor文
  7. 独立型2重ループ
  8. 従属型2重ループ
  9. フィルタ入り2重ループ
  10. 間に処理の入る2重ループ
  11. 2変数同時ループ
  12. 3重ループ
  13. ループの中で関数呼び出し
  14. ループの中で関数(ループあり)呼び出し
  15. 再帰呼び出しによるデクリメント
  16. 再帰呼び出しによるインクリメント

検証コードです.出力は各自どうぞ.

#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で割った商と余りに分解するとか,上位ビットと下位ビットに分解するとか.