7.6 多重ループ

○星プログラム

 ループの使い方を '*' によって様々なかたちを描く例題で試します。初めに横と縦に '*' を並べて描くプログラムを、それぞれ示します。実行結果から分かるように通常コンソールへの文字の出力は右から左、上から下へと行われます。

 

リスト test7_10.c
#include<stdio.h>
main()
{
int i;
for(i=0;i<5;i++){
printf("*");
}
}
 リスト test7_10.c の実行結果
  *****






 

 

リスト test7_11.c
#include<stdio.h>
main()
{
int i;
for(i=0;i<5;i++){
printf("*\n");
}
}
 リスト test7_11.c の実行結果
  *
  *
  *
  *
  *


 

 

それでは、図8のようにアスタリスクを表示するプログラムはどのように描けばよいでしょうか。この様な場合、リストtest7_10.cを更に5回繰り返すと考えるとうまく行きます。つまりリストtest7_12.cのような2重ループで実現できます。これは多重ループの基本です。ループ変数を工夫することにより様々なバリエーションが可能になります。

 

 

 *****

 *****

 *****

 *****

 *****

 

<図8>

 

 

リストtest7_12.c

#include<stdio.h>

main()          

{            

   int i,j;      

   for(i=0;i<5;i++){ 

       for(j=0;j<5;j++)  

           printf("*");

       printf("\n");   

   }         

}            

 

 次に、図9のようにアスタリスクを表示するプログラムを考えます。リストtest7_12.cの応用です。リストtest7_13.cのようになります。

 

 

 

*
 **
 ***
 ****
 *****

図9



 
   リストtest7_13.c
   #include<stdio.h>
   main()
   {
      int i,j;
      for(i=0;i<5;i++){
          for(j=0;j<=i;j++)
              printf("*");
          printf("\n");
      }    
   }    

 

図10のようにアスタリスクを表示するプログラムはどうでしょうか。if文を使い行と列の情報からキャラクタをスイッチします。

 

 *   

  *   

  *  

   *  

   * 

    

図10  

 

 

 

リストtest7_13.c

#include<stdio.h>

main()               

{                 

   int i,j;           

   for(i=0;i<5;i++){       

       for(j=0;j<5;j++){  

           if(i==j)   

               printf("*");

            else     

               printf(" ");

        }           

        printf("\n");    

    }             

}              

○時計プログラム

 多重ループの身近な例は時計でしょう。2重のループを使って分と秒を表示するプログラムを組み立てます。forループはそのままでは一瞬で終わってしまいますから、時間待ち関数Sleep()を使

います。

 更に、時報を追加してみましょう。if文を使って0秒目、57,58,59秒目、それ以外について音を切り替えます。clk00.c、clk01.cで使用したSleep()とBeep()はWindowsAPI関数と呼ばれ、Windows環境専用の関数です。WindowsAPI関数を使う時にはwindows.hをインクルードします。

 

リストclk00.c

#include<windows.h>

#include<stdio.h>

main()

{

  int m,s;

  for(m=0;m<60;m++){

    for(s = 0;s<60;s++){

  printf("%d min: %d sec\n",m,s);

  Sleep(1000);

}

  }

}

 

リスト clk01.c

#include<windows.h>

#include<stdio.h>

main()

{

  int m,s;

  for(m=0;m<60;m++){

for(s = 0;s<60;s++){

  printf("%d min: %d sec\n",m,s);

if( s == 0){

  Beep(880,100);

}

else{

  if( s >= 57){

Beep(440,100);

  }

  else{

Beep(1000,100);

  }

}

  Sleep(900);

}

  }

}

Sleep()

 

VOID Sleep( DWORD dwMilliseconds );

 

パラメータ dwMilliseconds

 実行を中断する時間を、ミリ秒単位で指定します。0 を指定すると、スレッドは、実行の準備が できている同じ優先順位のほかのスレッドに残りのタイムスライスを譲ります。そのようなスレ ッドがない場合は、関数はすぐに戻ります。INFINITE を指定すると、無制限に実行が中断され ます。

 

戻り値   戻り値はありません。

 

解説 WindowsAPI関数。現在のスレッドの実行を、指定された時間だけ中断します。

 

対応情報  Windows NT: バージョン 3.1 以降、Windows: Windows 95 以降、Windows CE: バージョン 1.0 以降

 

Beep()

 

BOOL Beep(

 DWORD dwFreq, // sound frequency, in hertz

 DWORD dwDuration // sound duration, in milliseconds

);

 

パラメータ

 dwFreq Windows NT:音の周波数をヘルツ単位(37 (0x25) 〜 32767 (0x7FFF))で指定しま す。Windows 95:このパラメータは無視されます。

 dwDuration Windows NT:音の継続時間をミリ秒単位で指定します。

   Windows 95:このパラメータは無視されます。

 

戻り値 関数が成功すると、0 以外の値が返ります。関数が失敗すると、0 が返ります。 拡張エラー情報を取得するには、GetLastError 関数を使います。

 

解説

 WindowsAPI関数。スピーカから単純な音を鳴らします。この関数は同期式です。音が終了しないかぎり制御を返しません。

 Windows 95/98/meではdwFreq パラメータと dwDuration パラメータは無視されます。サウンドカードがついたコンピュータでは、一般の警告音が鳴ります。サウンドカードのないコンピュータでは、標準ビープ音が鳴ります。

 

対応情報 Windows NT: バージョン 3.1 以降、Windows: Windows 95 以降

○サイン・グラフ・プログラム

 もう少し数学ぽっくsinのグラフを画いてみましょう。sin関数を求めるには算術関数のsin()を使用します。この場合ヘッダーファイルとして必ずmath.hをインクルードします。

 計算方法は次のようになります。

1)画面の20行をsinの1周期とするために0から19まで変化する変数iからsin()への角度引数を計算ます。

2)0から2πまでの角度を受け取ったsin()は、−1から1までの浮動小数点値を返します。

3)これを画面の80桁に合わせて、振れ幅±30、オフセット35の整数値に変換します。

4)この整数値をもとに'*'の位置を送ります。

 少しプログラムが複雑になってきたので、それぞれの役目をプログラム中にメモしておく方が得策です。C言語ではプログラム中に // を書く事によってその行の // 以降はコメントになります。当然ですが動作を確かめるだけならコメントはなくてもかまいません。

 

リスト test7_14.c

#include <math.h> // sin()の設定用

#include <stdio.h>

main()

{

    int   i,j,d;

    double  s;

    for(i=0;i<20;i++){ // 20行で1周期とする

        s=3.14*2.0 * (double)i /20.0; // iに対応する角度の計算

        d =(int)( 35.0 + 30.0 * sin(s)); // コマ送り数を計算

        for(j=0;j<d;j++) // 1行分のコマ送り

            printf(" ");

        printf("*\n"); // 最後に*を描いて改行

    }

}

 

<図11>リスト13の実行結果

 

                  *
                      *
                          *
                              *
                                *
                                *
                                *
                              *
                          *
                      *
                  *
             *
         *
     *
   *
   *
   *
     *
         *
             *

 

○各変数の動き

ii/20.0ssin d
000030
50.251.57(90度相当)0.9965
10   0.5   3.14(180度相当) 0 30

:        

15   0.75 4.71(270度相当)     -0.99   5

:

19   0.95 5.96(360度相当) 0    30

 

練習問題1. サイングラフのふれ幅を小さく変更せよ。

練習問題2. サイングラフの周期を2に変更せよ。

練習問題3. 初期位相を+90度つけた場合のグラフに変更せよ。

 

○サイン・コサイン・グラフ

 更にコサインも併せて描くにはどうすればよいでしょうか。1行の描画について考えると、サイン、コサインのどちらか大きい値まで描画が必要になります。そこでサイン、コサインの横方向コマ送り値の大きい方を取り出しループの回数にします。そして、そのループの中でループカウンタの値がサインあるいはコサインのコマ送り値になれば描画する文字をそれぞれ波形を表す文字に変更して出力します。

 

リストtest7_15.c

#include <math.h>

#include <stdio.h>

main()

{

int   i,j,d,e,m;

char c;

double s;

for(i=0;i<20;i++){

s = 3.14 * 2.0 * (double)i /20.0;

     d =(int)( 35.0 + 30.0 * sin(s));

e =(int)( 35.0 + 30.0 * cos(s));

m = d;

if(d < e ){ m = e; }

     for( j = 0 ; j <= m ; j++ ){

c = ' ';

if( j == d ){ c = 's'; }

if( j == e ){ c = 'c'; }

printf("%c" , c);

}

     printf("\n");

   }

}

 

実行結果

                  s               c

                      s         c

                          s   c

                          c   s

                      c         s

                  c              s

             c                   s

         c                     s

     c                     s

   c                   s

   c               s

   c         s

     c   s

     s   c

   s         c

   s              c

   s                   c

     s                     c

         s                     c

             s                   c

○ループの書き方 インデント(段ずらし)

 for文の2重構造プログラムを書く場合、リストAのように2つのfor文を行の最初から書くよりリストBのように、内のfor文を少し後へずらせて書く方がその構造がはっきりします。つまりfor文の内部で行う処理とfor文と同じレベルで行う処理が段をずらすことで視覚的にとらえることができるからです。このような書き方をインデント(段ずらし)といいます。

main()
{
int i,j,y[100][200],x[100][200];
for(i=0;i< 100;i++)
for(j=0;j< 200;j++)
y[i][j]=x[i][j];
puts("end");
}
   main()
   {
       int i,j,y[100][200],x[100][200];
       for(i=0;i< 100;i++)
          for(j=0;j< 200;j++)
            y[i][j]=x[i][j];
       puts("end");
   }

<リストA>インデントを行わない場合      <リストB>インデントを行った場合

練習問題1 右のような表示をするプログラムを作れ **

****   

                                ******

********

**********

 

練習問題2 右のような表示をするプログラムを作れ *****

                                ****

                                ***

**

*

 

練習問題3 右のような表示をするプログラムを作れ   *

  **

 ***

****

*****

 

練習問題4 右のような表示をするプログラムを作れ *  *

                                 * *

   *

* *

*  *

 

練習問題5 半径が * 10個となるような円形の右下1/4円     **********

                          **********

                                **********

                                **********

                                **********

                                ********* 

                                ******** 

                                ******** 

                                ******  

                                *****   

 

練習問題6 右のような表示をするプログラムを作れ ABCDE

                                FGHIJ

                                KLMNO

                                PQRST

UVWXY

 

練習問題7 p44の時報プログラムを参考に、10秒毎に880Hz、通常は440Hz、で発音するプログ   ラムを作れ

 

練習問題8 p44の時報プログラムを参考に、0秒目に100Hz、以後1秒毎に100Hzづつ発音周波   数が上昇するプログラムを作れ

 

練習問題9 p44の時報プログラムを参考に、0秒目に220Hz、以後順に半音づつ発音周波数が高く   なるプログラムを作れ。ヒント:半音は2の1/12乗、べき乗はpow(x,y)。

 

練習問題10 p44の時報プログラムを参考に、0秒目にドの音、以後順にド、レ、ミ、ファと発音   が高くなるプログラムを作れ。ヒント:初めのドの音を131Hzとする。

[雑学]音階と周波数

ご承知のように、1オクターブは12の半音に分解できます。1オクターブで周波数は2倍になります。ですから、半音では周波数は2の1/12乗倍になることになります。

 2の1/12乗の値はべき乗を求める関数 pow(x,y) を使って  pow( 2.0 , ( 1.0 / 12.0 ) ) で計算できます。