制御文は,コンピュータ・プログラミングの要(かなめ)のひとつである.
プログラムにおける処理は,main関数から始まって単純に上から下へと一本道で進むだけでない.
時と場合に応じて動作を変化させたり,同じような処理を何度も繰り返す必要がある.
このような処理の流れを制御するプログラム中の構文を,「制御文」という.
今回は,「条件分岐」と「繰り返し処理」の基本を学ぶ.
前回までの理解度の確認のために,簡単な練習問題を解いてみよう.
実行例(出力値は正しいとは限らない): (1) 3.1 <-数値を入力 1.9 <-出力 (2) 0.707 0.707 <-x,yをスペース区切りで入力 0.999 <-出力
1/2
ではなく,1.0/2.0
と書けば良い.(float)1 / (float)2
と書くことにより,一時的に実数に変換できる.(型キャストという)
このページ内へのリンクです.
コンピュータの動作の中で,高度な計算や制御を実現しようとする場合,何かの条件を元に判断をして,動作を臨機応変に変える必要がある.
例えば,エアコンの制御では,室内の温度や湿度を計測して,冷却する or 加熱するのかを決める.
また,スマーホフォンでは,タッチパネルを指で触ると,その位置や押し具合(圧力)でアプリを起動したり,文字を入力したりする.
このように,様々なセンサ類を用いた外界との相互作用や,計算の結果に応じて処理を中止したりなどの対処を変えるようなことがコンピュータには求められる.
これらは皆,コンピュータが何らかの条件を判断して,それに応じて処理を変化させている結果である.
このような動作を「条件分岐」と呼ぶ.
if
文(いふぶん と読む)は,条件文と呼ばれるC言語の構文である.
if
文には,判断の基準となる式(条件式 という)を記述し,その評価結果が 真 (true) または 偽 (false) のいずれになるかによって,処理の流れを変える動作をする.
if
文には大きく分けて三種類あるが,最も単純な形は以下のようになる.
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("This message is never displayed.\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 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 0 |
「aが3以上で,かつ,bは-2以下」は
( a >= 3 ) && ( b <= -2 )
と書く.
演算子には優先順位があり,この場合,&&
よりも不等号のほうが優先順位が高いので ( )
は不要であるが,
見やすさのため ( )
をつけておくとよい.
数学などでよく出てくる値の範囲を表す表現,「 n が 10 以上 20 未満」は,「 n が 10 以上,かつ,n が 20 未満」と解して
( 10 <= n ) && ( n < 20 )
と書く.
同様に,これの否定である「 n が 10 未満 または 20 以上」は,
( n < 10 ) || ( 20 <= n )
と書ける.
あるいは,これと等値な
! ( ( 10 <= n ) && ( n < 20 ) )
と書いてもよい.(ド・モルガンの法則ですね.)
重要:if( 10 <= n < 20 )
という範囲指定は誤り
例えば,n = 0の場合を考える.もちろん「偽」であるべきだが,以下のような処理により誤った結果となる.
結果的に条件式全体が「真」と判定され,正しい条件判定ができない.
やっかいなことに,この条件式自体はC言語の文法的に OK のため,コンパイルエラーが出ないので気が付きにくい.
まず,整数の値をキーボードから入力し,整数型変数 n に代入する.
この n に対して,以下の判定を行い画面に表示してみよう.
単純なif
文は,ある処理を「する」か「しない」かの判定であったが,場合によっては処理の二者択一を行いたい場合がある.
if
に続けて else
を組み合わせる構文を組み合わせることによって,これを記述することができる.
if文 その2
if(条件式) {
文1;
} else {
文2;
}
この構文では,if
文中の「条件式」を評価し,「真」であれば 文1 だけを実行し,「偽」であれば 文2 だけを実行する.
つまり,排他的に二者択一の処理を行う.
実行例: n=? -1 negative n=? 5 positive
実行例: n=? 4 even
実行例: n=? 95 9 is odd number
計算機の最大の利点は,人間にとっては苦痛な単純繰り返し作業を超高速で,間違いなく,文句を言わずに実行できる点にある.
処理の繰り返し構文を上手に使うことにより,計算機の本領を発揮できる.
プログラミングの世界では,繰り返し処理のことをループ処理と呼ぶ.
また,ソースコードの中で繰り返し実行される部分をループと呼ぶ.
特に,学術計算の分野では,行列計算(連立方程式)や,数値積分などを単純な四則演算に置き換えて,実行することにより,高度な計算結果を得ることが日常的に行われている.
反復処理が必要な応用例
機械学習のような一見,複雑な処理や問題も,簡単な処理の繰り返しに分割し,計算機で処理するということが広く行われている.
このような計算機のための解法手順のことをアルゴリズムという.
C言語では,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
ループでは,if
文と同様,まず条件式が評価され,その真偽に応じて繰り返し処理の継続か終了かを判定する.
これとは対照的に,以下に示す do ... while()
ループでは,まず1回目のループ処理を無条件に実行し,その後,式の評価を行う.
do {
文; // ループ処理の最初の1回は,必ず実行される.
} while(条件式); // このセミコロンを忘れないこと
注意:do-while()
は,最後のwhile
の行末のセミコロンを忘れないこと.
一例として,キーボード入力ミス判定や,何らかの計算処理など,最初に1度実行してみて,その結果に応じて繰り返しを「する」か「しない」かを判断する場合に適している.
do - while
ループを用いて,キーボードから整数を繰り返し入力し, 0 が入力されたら入力を終了するプログラムを作成しよう.
言い換えると,「入力された値が 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
文を用いたサンプルコードには,繰り返し処理のエッセンスが含まれている.
それは
これらの定型表現を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; // sum に次々と足してゆく.
}
// ループ処理が終了したので結果を表示.
printf("sum = %d\n", sum);
return 0;
}
なお,for(int i=1; i<=10; i++) {
のように,カウンタ変数の宣言をfor文内に書くこともできる.
カウンタ変数にはどのような名前をつけても構わないが,慣例では,i, j, k
などが用いられることが多い.
++
は,i
の値を1増やすインクリメント演算子といい,i += 1;
,i = i+1;
と等価である.
--
は,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; /* 前置インクリメントという */
この場合,
インクリメント ++
は,代入より先に実行され,i に 1 を加えてから j に代入される.
したがって,j の値は 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