制御文は,コンピュータ・プログラミングの要(かなめ)のひとつである.
プログラムにおける処理は,単純に1本道で進むだけでなく,時と場合に応じて動作を変化させたり,同じ処理を何度も繰り返す必要がある.
このような処理の流れを制御するプログラム中の構文を,「制御文」という.
今回は,「条件分岐」と「繰り返し処理」の基本を学ぶ.
前回までの理解度の確認のために,簡単な練習問題を解いてみよう.
実行例(出力値は正しいとは限らない): 3.1 <-数値を入力 1.9 <-出力 0.707 0.707 <-数値2つをスペース区切りで入力 0.999 <-出力
1/2
ではなく,1.0/2.0
と書けば良い.このページ内へのリンクです.
コンピュータの動作の中で,高度な計算や制御を実現しようとする場合,何かの条件を元に判断をして,動作を臨機応変に変える必要がある.
例えば,エアコンの制御では,室内の温度や湿度を計測して,冷却する or 加熱するのかを決める.
また,スマーホフォンでは,タッチパネルを指で触ると,その位置や押し具合(圧力)でアプリを起動したり,文字を入力したりする.
このように,様々なセンサ類を用いた外界との相互作用や,計算の結果に応じて処理を中止したりなどの対処を変えるようなことがコンピュータには求められる.
これらは皆,コンピュータが何らかの条件を判断して,それに応じて処理を変化させている結果である.
このような動作を「条件分岐」と呼ぶ.
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");
}
実際の 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
は実行されず,画面には何も表示されない.
x
の値をキーボードから入力するよう変更し,その値が正であれば「Positive」と画面に表示してみよう.
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 |
---|---|---|---|---|
「aが3以上で,かつ,bは-2以下」は
( a >= 3 ) && ( b <= -2 )
と書く.
演算子には優先順位があり,この場合,&&
よりも不等号のほうが優先順位が高いので ( )
は不要であるが,
見やすさのため ( )
をつけておくとよい.
「 n が 10 以上 20 未満」は,「 n が 10 以上,かつ,n が 20 未満」と解して
( 10 <= n ) && ( n < 20 )
と書く.
まず,整数の値をキーボードから入力し,整数型変数 n に代入する.
この n に対して,以下の判定を行う.
と画面に表示してみよう.
重要:( 10 <= n < 20 )
という範囲指定は誤り
例えば,n = 0の場合を考える.もちろん範囲外なので「偽」であるべきだが,
結果的に条件式全体が「真」と判定され,正しい条件判定ができない.
(この式はC言語の文法的には問題ないため,コンパイル時にエラーが出ないので気が付きにくく,とても厄介である.)
単純なif
は,ある処理を「する」か「しない」かの分岐だけであったが,場合によっては処理の二者択一を行いたい場合がある.
if
文に続けてelse文を組み合わせることによって,これを記述することができる.
(この場合も,インデント=字下げをきれいに書くこと.)
if(式) {
文1;
} else {
文2;
}
この構文では,if
文中の「式」を評価し,「真」であれば文 1 だけを実行し,「偽」であれば文 2 だけを実行する.
整数を入力: 95 10の位は,奇数です.
計算機の最大の利点は,人間にとっては苦痛な「単純繰り返し作業」を超高速で,間違いなく,文句を言わずに実行できる点にある.
処理の「繰り返し」構文を上手に使うことにより,計算機の本領を発揮できる.
プログラミングの世界では,繰り返し処理のことを「ループ処理」と呼ぶ.
また,ソースコードの中で繰り返し実行される部分を「ループ」と呼ぶ.
特に,学術計算の分野では,行列計算(連立方程式)や,数値積分などを単純な四則演算に置き換えて実行することにより,高度な計算結果を得ることが日常的に行われている.
反復処理が必要な応用例
機械学習のような一見,複雑な処理や問題も,簡単な処理の繰り返しに分割し,計算機で処理するということが広く行われている.
このような計算機のための解法手順のことを「アルゴリズム」という.
ここでは,while
文,do-while
文,for
文を学ぶ.
繰り返しの一番単純な形は,while
文(while
ループ)であり,以下のように記述する.
書式については,条件分岐である if
文と似ている.
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]キーを押すと,強制終了できる.
上記プログラムにおいて,ループの範囲を以下のように変更してみよう.
while
文において,条件式が常に「真」となるような記述すると,無限に処理を繰り返すことができる.
while( 1 ) { // 1でなく,2でも3でも良いが,通常は 1 を使う.while(true) もOK.
... // 無限に繰り返す処理
}
このような技法を「無限ループ」と呼ぶ.
一見すると間違った処理にも見えるが,マイコンを使ったロボット制御など,「電源がONの間はプログラムが終了してはいけない処理」で用いられる.
また,break
と組み合わせることにより,一定の条件やタイミングで,無限ループから脱出するような記述も可能である.
無限ループを用いて,画面に Hello World!
を無限に表示するプログラムを作成しよう.
何個表示したかを確認できるよう,カウンタ変数を用いて数値を表示してみよう.
Hello World! 1 Hello World! 2 Hello World! 3 Hello World! 4 . . .
止め方は,ターミナルで [CTRL] + [C] キー.
while
ループでは,はじめに条件式が評価され,その真偽に応じて繰り返し処理の継続か終了かを判定する.
これとは対照的に,以下に示す do ... while
ループでは,式の評価はループ中の文を実行した「後」に行う.
do {
文;
} while(条件式); // このセミコロンを忘れないこと
注意:do-while()
は,最後のwhile
の行末のセミコロンを忘れないこと.
一例として,キーボード入力や計算処理など,最初に1度実行してみて,その結果に応じて繰り返しを「する」か「しない」かを判断する場合に適している.
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!
while
文を用いたサンプルコードには,繰り返し処理のエッセンスが含まれている.
それは
これらをまとめて書くことのできる構文が,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 にしてから代入される.
while, do-while
文の使い分けはどう考えればよいですか?while
を使用します.do-while
です.while, for
文の使い分けはどう考えればよいですか?以下の問それぞれに対応するプログラムを作成しなさい.提出不要.
キーボードから入力した自然数が,奇数か偶数かを判定するプログラムを作成しなさい.
ゼロや負数が入力されたらエラーを表示すること.
実行例: Input a number : 17 17 is odd. Input a number : 2 2 is even. Input a number : -5 Error! Input a number : 0 Error!
まず,1 ∼ 99 までの値を順に表示させよう.
次に,3の倍数と 3 が付く数字に,[カッコ] をつけて表示してみよう.
実行例: 1 2 [3] 4 5 [6] 7 8 [9] 10 11 [12] [13] 14 ... 29 [30] [31] [32] ... 98 [99]
ユーザーにキーボードから自然数を繰り返し入力させ,0 が入力されたら入力処理を終了し,
それまで入力した全ての数値の合計値を表示するプログラムを作成せよ.
ヒント1:あらかじめ合計値を記憶しておく整数型の変数 sum
を用意し,0 で初期化しておき,数値を入力するごとにここに値を足してゆく.
ヒント2:do-while
文が最適.
実行例: Input? 1 Input? 2 Input? 3 Input? 4 Input? 0 ---- end ---- sum = 10