制御文(1):条件分岐と繰り返しの基礎

制御文は,コンピュータ・プログラミングの要(かなめ)のひとつである.
プログラムにおける処理は,単純に1本道で進むだけでなく,時と場合に応じて動作を変化させたり,同じ処理を何度も繰り返す必要がある.
このような処理の流れを制御するプログラム中の構文を,「制御文」という.
今回は,「条件分岐」と「繰り返し処理」の基本を学ぶ.

理解度チェック

理解度チェック

前回までの理解度の確認のために,簡単な練習問題を解いてみよう.

  1. 変数 x, または x,y の値をキーボードから入力し,以下の値を出力してみよう.
    • x2 - 2x + 1
    • x2 + y2
      実行例(出力値は正しいとは限らない):
    
      3.1   <-数値を入力
      1.9   <-出力
    
      0.707 0.707   <-数値2つをスペース区切りで入力
      0.999   <-出力
      
  2. 1 + 1/2 + 1/4 + 1/8 + ... を,なるべく高精度で計算してみよう.(答えはもちろん2ですね)
    ここでは,繰り返し文を使わないで,式をそのままプログラム内に記述してみよう.
    ヒント:整数/整数 は,切り捨てになってしまうので,実数で計算する.つまり,1/2 ではなく,1.0/2.0と書けば良い.

目次

このページ内へのリンクです.

条件分岐の基礎

コンピュータの動作の中で,高度な計算や制御を実現しようとする場合,何かの条件を元に判断をして,動作を臨機応変に変える必要がある.

例えば,エアコンの制御では,室内の温度や湿度を計測して,冷却する or 加熱するのかを決める. また,スマーホフォンでは,タッチパネルを指で触ると,その位置や押し具合(圧力)でアプリを起動したり,文字を入力したりする.

このように,様々なセンサ類を用いた外界との相互作用や,計算の結果に応じて処理を中止したりなどの対処を変えるようなことがコンピュータには求められる.
これらは皆,コンピュータが何らかの条件を判断して,それに応じて処理を変化させている結果である.
このような動作を「条件分岐」と呼ぶ.

if 文

if文(いふぶん と読む)は,条件文と呼ばれるC言語の構文である. if文では,判断の基準となる式(条件式 という)を記述し,その評価結果が真 (true) または 偽 (false) のいずれになるかによって,処理の流れが決まる.

if文の最も単純な形は,以下のようになる.

if (条件式) {
    文;
}

このプログラムにおいて,「文」の処理は,

そして,それ以降の行へと処理が続行する.

また,if文の条件式(単に式ともいう)は,以下の比較演算子論理演算子を組み合わせて用いる.

比較演算子

比較演算子は2つの値の比較を行う演算子であり,関係演算子とも呼ばれる.

演算子 関係
> ∼ より大きい
>= ∼ より大きい,または,等しい. (=> の順だとエラー
== 等しい. ( = だと代入!
!= 等しくない.
<= ∼ より小さいか,等しい. (=<の順だとエラー
< ∼ より小さい

例えば, 3 > 2 という式は,真(true)である,という.
逆に,2 > 3 という式は,偽(false)である,という.

if (3 > 2) {
    printf("3 is greater than 2\n");
}

練習問題1

実際の if 文を見てみよう.
以下のプログラムを入力して,実行してみよう.

#include <stdio.h>

int main(void)
{
    int x;

    x = 10;

    if(x > 2) {                  /* 条件式を判定 */
        printf("It's true.\n");   /* 真の場合のみこの文を実行.字下げして見やすく書くこと. */
    }

    return 0;
}

これを実行すると画面に「It's true.」と表示される. なぜならば,if 文の ( )内の条件式が「真」だからである.

一方,x の値を変更した次の例

x = 1;
if(x > 2) {
    printf("この行は実行されない.\n");
}

では,if 文が偽となるので,次行の printf は実行されず,画面には何も表示されない.

練習:
  1. 整数 x の値をキーボードから入力するよう変更し,その値が正であれば「Positive」と画面に表示してみよう.
  2. おなじく,キーボードから入力した整数の値が正であれば「Positive」,負であれば「Negative」,と画面に表示してみよう.0は考えなくても良い.

bool型について

C言語では,「真」=0 以外の値,「偽」= 0 と,整数で定義されている.
そこで,C++言語では,真または偽を直接的に表す bool 型という変数が,新しく追加された.
bool 型の変数は,true または false の,どちらかの値のみをとる.

使用例int main(void)
  {
      bool kisu = false; // falseで初期化
      int i = 3;
  
      if( i % 2 == 1 ) {     // i を2で割った余りが1 = iが奇数ならば
          kisu = true;
      }
  
      return 0;
  }

コードブロック

上記の,条件分岐の処理に用いた { } の区間を「コードブロック」または単に「ブロック」と呼ぶ.
コードブロックは,「複文」とも呼ばれ,if文や,後述するfor文などの制御文の影響範囲を指定することができる.

修飾する範囲が1行のみの場合は,以下のように { } を省略することができる.

if(式1) {     /* ここから */
  文1;
  文2;
  文3;
  文4;
}             /* ここまで */


文が1行のみの場合は,{}を省略可.

if(式1)
    文1;

main 関数の始まりと終わりの{ }も,実は関数の開始と終了を示すコードブロックを表している.

字下げ(インデント)について

通常,ブロック部では,階層に応じて半角スペース4文字分のインデントを行っている.(2文字や,タブの場合もある)
実は,C言語の文法規則では,字下げは必要ではない.
しかし,ソースコードの見やすさを確保するために,ぜひ奇麗なインデントを行うよう心がけよう.
一方,Python言語は字下げによってコードブロックを指定する仕様であり,ソースコードの字下げを奇麗に書かないと正しく動作しない言語もある.

Cでは,このように字下げを汚く書いても問題はないが,字下げが揃っていないと読みにくく間違いの元.

    if(式1) {

文1;
      文2;
文3;
          文4;
    }

論理演算子

少々複雑な条件分岐を行おうとすると,単独の関係演算子では不十分である. そこで,条件を組み合わせることで,より高度な判定を行うことができる.
例えば,言葉で表現すると

など,いくらでもある. これらの表現のように,「かつ」「または」「∼ でない」をプログラム中で表す方法が論理演算子である.

論理演算子は,AND, OR, NOT の3種があり,真偽の演算の結合を行うことができる.

プログラム中での書き方
(記号間にスペースを入れない)
説明
&&
AND(論理積)
||
OR(論理和)
!
NOT(否定)

具体的な論理演算の結果を次の表に示す. 真偽値 a,b にたいして,出力 0 は false,1 は true とする.

a b a && b a || b ! a
0
0
0
0
1
0
1
0
1
1
1
0
0
1
0
1
1
1
1
0

例1

「aが3以上で,かつ,bは-2以下」は ( a >= 3 ) && ( b <= -2 ) と書く.
演算子には優先順位があり,この場合,&& よりも不等号のほうが優先順位が高いので ( ) は不要であるが, 見やすさのため ( ) をつけておくとよい.

例2

「 n が 10 以上 20 未満」は,「 n が 10 以上,かつ,n が 20 未満」と解して ( 10 <= n ) && ( n < 20 ) と書く.

練習問題2

まず,整数の値をキーボードから入力し,整数型変数 n に代入する.
この n に対して,以下の判定を行う.

と画面に表示してみよう.

数値の範囲の表し方

重要:( 10 <= n < 20 ) という範囲指定は誤り

例えば,n = 0の場合を考える.もちろん範囲外なので「偽」であるべきだが,

  1. まず10 <= nが判定され,偽であるから,この部分が数値0に置き換えられる.
  2. 次に,0 < 20 が評価され,これは真と評価される.

結果的に条件式全体が「真」と判定され,正しい条件判定ができない.
(この式はC言語の文法的には問題ないため,コンパイル時にエラーが出ないので気が付きにくく,とても厄介である.)

if~else 文

単純なif は,ある処理を「する」か「しない」かの分岐だけであったが,場合によっては処理の二者択一を行いたい場合がある.
if 文に続けてelse文を組み合わせることによって,これを記述することができる.
(この場合も,インデント=字下げをきれいに書くこと.)

if(式) {

    文1;

} else {

    文2;

}

この構文では,if文中の「式」を評価し,「真」であれば文 1 だけを実行し,「偽」であれば文 2 だけを実行する.

練習問題3

  1. 整数型変数 x を定義し,値をキーボードから入力し,正の値であれば「正です」,負の値であれば「負です」と表示してみよう.
    0 は入力しないものとする.
  2. キーボードから整数を入力し,奇数・偶数の判定をしよう.奇数であれば「奇数です」,偶数であれば「偶数です」と画面に表示する.
    ヒント:偶数とは,その数を 2 で割った余りが 0 に等しい,と考える.
    余りを求める演算子は...?
  3. 同様に,キーボードから2桁の整数を入力し,10の位の数値が奇数か偶数の判定をしよう.
    整数を入力: 95
    10の位は,奇数です.
    

繰り返し処理の基礎

計算機の最大の利点は,人間にとっては苦痛な「単純繰り返し作業」を超高速で,間違いなく,文句を言わずに実行できる点にある.
処理の「繰り返し」構文を上手に使うことにより,計算機の本領を発揮できる.

プログラミングの世界では,繰り返し処理のことを「ループ処理」と呼ぶ.
また,ソースコードの中で繰り返し実行される部分を「ループ」と呼ぶ.

特に,学術計算の分野では,行列計算(連立方程式)や,数値積分などを単純な四則演算に置き換えて実行することにより,高度な計算結果を得ることが日常的に行われている.

反復処理が必要な応用例

機械学習のような一見,複雑な処理や問題も,簡単な処理の繰り返しに分割し,計算機で処理するということが広く行われている.
このような計算機のための解法手順のことを「アルゴリズム」という.

ここでは,while文,do-while文,for文を学ぶ.

while文

繰り返しの一番単純な形は,while文(whileループ)であり,以下のように記述する.
書式については,条件分岐である if 文と似ている.

while ( 条件式 ) {
    文;
}

処理の流れを細かくみると,

具体例:
// 5 から 12 までの整数を画面に表示する

#include <stdio.h>

int main(void)
{
    int i;                             // 1.繰り返しのために,整数型変数を用意.カウンタ変数という.

    i = 5;                             // 2.i に初期値を代入 

    while(i <= 12) {                   // 3.i が 12 以下なら,繰り返しを続ける.

        printf("i = %2d \n", i);       // %2d = 2桁で表示

        i++;                           // 4.一回繰り返す毎に,iに1を足す.

    }   // これが while ループの終わり

    return 0;
}

実行結果

i =  5
i =  6
i =  7
i =  8
i =  9
i = 10
i = 11
i = 12

注:プログラムのミス等でwhileの条件式が永久に偽にならない場合は,処理が無限に繰り返される.
この場合は,ターミナル画面を選択した状態で,キーボードの[CTRL]キーを押しながら[c]キーを押すと,強制終了できる.

練習問題4

上記プログラムにおいて,ループの範囲を以下のように変更してみよう.

  1. 0 から 10 まで
  2. 0 から 30 まで,3 ごと (e.g. 0, 3, 6, ... , 21)
  3. 10 から 0 まで,-2 ごと.(カウントダウン)

無限ループ

while 文において,条件式が常に「真」となるような記述すると,無限に処理を繰り返すことができる.

while( 1 ) {   //  1でなく,2でも3でも良いが,通常は 1 を使う.while(true) もOK.

    ... // 無限に繰り返す処理

}

このような技法を「無限ループ」と呼ぶ. 一見すると間違った処理にも見えるが,マイコンを使ったロボット制御など,「電源がONの間はプログラムが終了してはいけない処理」で用いられる.
また,breakと組み合わせることにより,一定の条件やタイミングで,無限ループから脱出するような記述も可能である.

練習問題5

無限ループを用いて,画面に Hello World! を無限に表示するプログラムを作成しよう.
何個表示したかを確認できるよう,カウンタ変数を用いて数値を表示してみよう.

Hello World!  1
Hello World!  2
Hello World!  3
Hello World!  4
.
.
.

止め方は,ターミナルで [CTRL] + [C] キー.

do-while文

whileループでは,はじめに条件式が評価され,その真偽に応じて繰り返し処理の継続か終了かを判定する.
これとは対照的に,以下に示す do ... while ループでは,式の評価はループ中の文を実行した「後」に行う.

do {

    文;

} while(条件式);        // このセミコロンを忘れないこと

注意:do-while()は,最後のwhileの行末のセミコロンを忘れないこと.

一例として,キーボード入力や計算処理など,最初に1度実行してみて,その結果に応じて繰り返しを「する」か「しない」かを判断する場合に適している.

練習問題6

do - while ループを用いて,キーボードから整数を繰り返し入力し, 0 が入力されたら入力を終了するプログラムを作成しよう.

実行例(以下のように続けて入力させるように)

Input a number: 5    <- 入力
The number was 5     <- 出力

Input a number: 100
The number was 100

Input a number: -10
The number was -10

    .
    .
    .

Input a number : 0
The number was 0

Finish!

for文

while文を用いたサンプルコードには,繰り返し処理のエッセンスが含まれている.
それは

  1. 繰り返し回数を数えるため,整数型変数を用意.(カウンタ変数と呼ぶ)
  2. カウンタ変数に初期値を設定.
  3. 繰り返しを「続ける条件」を条件式として書く.
  4. 1回繰り返すごとにカウンタ変数を更新.
    (数値を足す,引く,かける,わるなど.数値を増加させることを「インクリメント」という.逆に減ずることを「デクリメント」という.)

これらをまとめて書くことのできる構文が,forループである.

for ( 初期化部 ; 条件式 ; インクリメント部 ) {
    文;
}

for 文を用いた常套句を示す.
この例では,1 から 10 までの値の2乗を合計している.

#include <stdio.h>

int main(void)
{
    int i;          // カウンタ変数
    int sum = 0;    // 計算用変数.以下で += で加算してゆくので,最初に初期化が必要

    for(i=1; i<=10; i++) {    // よくある書き方.

        sum += i*i;

    }

    printf("sum = %d\n", sum);
	
    return 0;
}

なお,for(int i=1; i<=10; i++) {のように,カウンタ変数の宣言をfor文内に書くこともできる.
カウンタ変数にはどのような名前をつけても構わないが,慣例では,i, j, k などが用いられることが多い.

++は,iの値を1増やすインクリメント演算子といい,i = i+1;と等価である.
--は,iの値を1減ずるデクリメント演算子といい,i = i-1;と等価である.


インクリメント演算子の位置による結果の違い

インクリメント・デクリメント演算子は変数の「前」にも「後」にも置くことができる.
i++, ++i を単独で使用する場合には違いはないが,代入演算と合わせて記述する際には,演算の実行順序に気をつける必要がある.

#include <stdio.h>

int main(void)
{
    int i = 0;
    int j = i++;      /* 後置インクリメントという */
    printf("i=%d j=%d\n", i, j);
    return 0;
}

上記の演算の場合,j の値は 0 である.
インクリメントの演算は,i の値を j に代入した「後」に行われる.

int j = ++i;      /* 前置インクリメントという */

この場合結果は異なり,j の値は 1 である.
インクリメントは代入に先んじて実施され,i を 1 にしてから代入される.

Q and A

Q and A
while, do-while 文の使い分けはどう考えればよいですか?
ループの継続判定が,ループ処理の先に来るか,あとに来るかの違いで使い分けます.
条件式の判定の結果,一度も反復処理をしない可能性がある場合は whileを使用します.
逆に,少なくとも一回はループ内の処理をしないと,繰り返すかどうか判定できない場合は do-while です.
while, for 文の使い分けはどう考えればよいですか?
どちらを用いても同じ処理ができます.
目安として,初めから繰り返し回数がわかっている場合や,単純に数を数えあげるような繰り返しはfor文で,逆に処理に応じていつ反復が終わるかまちまちなループ処理はwhileでしょうか.

実習の進め方

練習問題(授業時間内に実施)

以下の問それぞれに対応するプログラムを作成しなさい.提出不要.

Q1 if文

キーボードから入力した自然数が,奇数か偶数かを判定するプログラムを作成しなさい.
ゼロや負数が入力されたらエラーを表示すること.

実行例:

Input a number : 17
17 is odd.

Input a number : 2
2 is even.

Input a number : -5
Error!

Input a number : 0
Error!

Q2 if, forの入れ子

まず,1 ∼ 99 までの値を順に表示させよう.
次に,3の倍数と 3 が付く数字に,[カッコ] をつけて表示してみよう.

実行例:
1 2 [3] 4 5 [6] 7 8 [9] 10 11 [12] [13] 14 ... 29 [30] [31] [32] ... 98 [99]

Q3 do-while文

ユーザーにキーボードから自然数を繰り返し入力させ,0 が入力されたら入力処理を終了し, それまで入力した全ての数値の合計値を表示するプログラムを作成せよ.

ヒント1:あらかじめ合計値を記憶しておく整数型の変数 sumを用意し,0 で初期化しておき,数値を入力するごとにここに値を足してゆく.
ヒント2:do-while 文が最適.

実行例:

Input? 1
Input? 2
Input? 3
Input? 4
Input? 0
---- end ----
sum = 10