9.制御構文 – ループ
特定の処理を繰り返し実行するのがループです。
ループは何らかの条件を満たすまで処理を繰り返させるため、比較演算子や論理演算を使用して、その結果がtrueかfalseかでループ続行の判断を行います。
ループを書くにはいくつかの方法があります。
for文
for文は、ループさせる回数が分かっている時に使用するループです。
for(初期値; 条件式; 増分){ ループさせる処理 }
のように書きます。
処理の流れとしては、
- 初期値を用意
- 条件式を評価
- trueならループさせる処理を実行
- 増分を処理
- 条件式を評価
- trueならループさせる処理を実行
- …
- falseなら終了
となります。
例えば、10回ループさせる場合の処理は、以下のように書きます。
for(int i=0; i<10; i++){ Print(i); }
初期値として、int i=0
条件式として、i<10
増分として、i++
を書いています。
ちょっとごちゃっとしていますが、
- int型の変数iを用意し、0を代入
- iが10より小さい間はPrint処理でiを表示する
- iを1増やす
という処理になります。
実行すると、
0 1 2 3 4 5 6 7 8 9
と表示されます。
初期値として用意するint型の変数名が「i」なのは慣例的なもので、こうしなければならないわけではありません。
「i」なのは、indexの頭文字だと思われます。
また、初期値が1ではなく0なのにも理由があります。
配列の章で触れたように、ループ処理は配列と相性が良いです。
10個の配列を順番に処理するといった場合、配列のインデックスは0から始まるので、変数iを配列のインデックスとして利用することができます。
double numbers[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(int i=0; i<10; i++){ Print(numbers[i]); }
変数iに代入されている値は0から9まで増えるので、このように書くとnumbers[0]からnumbers[9]までを順番に処理することができます。
また上記では配列の要素数が10であることが分かっているので、ループの条件式をi<10としていますが、この”10”はマジックナンバーです。
配列の要素数が変更されればここも書き換える必要があり、あまりスマートな書き方ではありません。
配列の要素の数だけループさせたい場合は、ArraySizeという関数を使用することが推奨されます。
double numbers[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(int i=0; i<ArraySize(numbers); i++){ Print(numbers[i]); }
ArraySize(numbers)で、配列numbersの要素数を取得しています。
このように書けば、配列の要素数がいくつであろうとその分だけループ処理が行われるようになります。
突然出てきた「関数」については、次章で解説します。
また、初期値・条件式・増分をひっくり返すことで、配列を逆から順に処理することもできます。
double numbers[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(int i=ArraySize(numbers)-1; i>=0; i--){ Print(numbers[i]); }
このように書くと、
- 初期値は、配列numbersの要素数-1(要素数が10なら9が初期値になる)
- 条件式は、iが0以上の時(iが-1になったらループを抜ける)
- 増分は、iを1ずつ減らす
となり、numbers[9]からnumbers[0]までを順番に処理することができます。
また、ループ処理内にループ処理を記述するケースがあります。
10回ループの中に10回ループを書く場合、外側のループをA、内側のループをBとすると、
- Aの1回目でBが10回ループ
- Aの2回目でBが10回ループ
- …
- Aの10回目でBが10回ループ
となります。
例えば、
for(int i=1; i<10; i++) { for(int j=1; j<10; j++) { Print(i * j); } }
このように書くと、かけ算の九九のような表示になります(1の段~9の段までの数字が順番に表示される)。
while文
while文は、ループさせる回数が不明な場合に使用するループです。
while(条件式){ ループさせる処理 }
のように書きます。
条件式が満たされている間、ループさせる処理が繰り返し実行されます。
for文のように初期値や増分が書かれていませんが、少なくともいつかは条件式が満たされる必要があります。
前述のfor文のループをwhile文で書くなら、以下のようになります。
double numbers[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int i=0; while(i<ArraySize(numbers)){ Print(numbers[i]); i++; }
処理結果は全く一緒になります。
実際、どちらのループを使っても表現できないという事はありません。
なので、どちらを使っても構わないのですが、ケースバイケースでどちらが書きやすいかが変わります。
もう少しwhile文の例を挙げておきます。
除算演算子を使わず、ループと引き算を使って、割り算500/7の答えを求めてみます。
int number = 500; int count = 0; while(number >= 7){ number -= 7; count++; } Print(count);
変数numberから7を引き続けて、その回数を変数countでカウントしています。
これを変数numberが7未満になるまで繰り返し、ループが終わった後の変数countには、割り算500/7の答えが代入されています。
while()の部分に記述する条件式が特に無い場合は、以下のように記述するケースもあります。
while(true){ ループさせる処理 }
条件式にtrueとだけ書いた場合、常に条件が満たされていることになり、無限ループになります。
無限ループは、本当に無限に終わらないので、プログラムがこのループに入った時点で見た目上プログラムは停止します(MT4/5は応答なしの状態になります)。
このような無限ループを抜けさせるには、条件分岐のswitch文でも使用したbreakを使います。
while(true){ ループさせる処理 if(条件式){ break; } }
このように、ループさせる処理の中に別の条件式を仕込み、その条件が満たされた場合にbreakするようにします。
breakが実行されるとそこでループが終了します。
ちなみにbreakはfor文の中でも使用することができます。
条件が複雑になってくると、正しくループしてくれるのかどうか、パッと見では判断しにくくなることがあります。
特に無限ループに陥ると、MT4/5を強制終了する羽目になったりするので、非常に面倒です。
このように少し注意点はありますが、ループは非常に強力な制御構文ですし、うまく使えばコーディングがとても楽になります。
ループ続行・終了の条件がどのように変化していくかをしっかり確認し、ループをマスターしましょう。
以上がループです。
次回は、ブロックとスコープについて解説します。