C++によるプログラミング入門11
while文とfor文

こんにちは。前回の最後に、短いプログラムならクラスを使わない方が楽のようでも、長いプログラムになると逆にクラスを使ったプログラムの方が楽になる、というようなことを書きました。これについてもう少し補足します。

みなさんはクラスというものをどのように思いましたか。クラスはプログラマが独自に定義した「型」です。この「型」を使うことで、いくらでも「オブジェクト」がつくれます。つまり、

    クラス名 変数(あれば引数);

と書くだけで、オブジェクト(インスタンスともいう)が生成され、変数がそのオブジェクトを表すのでした。このような「クラス」の使い方はいろいろありますが、基本的には、「プログラムの部品」(あるいは、オブジェクトを部品と考えると、「部品の設計図」)であると言えるでしょう。
 この「部品」はまたプログラム本体とかなり独立なものです。つまり、一度書いたクラスはそのまま別のプログラムでも使えるはずなのです。このように他の部分と比較的独立していて使いまわしが出来るということは「クラス」の大きな長所なのです。
 まず、第一に他人と協力してプログラムを組むのに便利です。例えば、動物園のプログラムを書くとしましょう。このとき、クラスとして「熊」や「ライオン」や「象」をそれぞれ別のプログラマが書いて持ちよるということが出来ます。
 第二に、自分ひとりでプログラムを書く場合でも、プログラムをつくりやすくなります。俗に「半年前に書いたコードは他人が書いたコードと同じ」と言います。ちょっとプログラムを書くとこれはすぐ実感できるのではないでしょうか。クラスを使えばそうではなくなる...と言うつもりはありません。それでも、プログラムが明白に部品に分けられているので、後で読んでも理解しやすいのです。自分の書いたプログラムが理解しやすいも何もないものだと思うかもしれませんが、実際これは重要です。
 前回のおみくじプログラムでは、「おみくじ」をわざわざクラスにした理由はあまりわかりません。しかし、例えば、おみくじと猫が関連したプログラムをつくるならこのクラス「おみくじ」とクラス「猫」をそのまま持ってきて、それらを組み合わせるプログラムを書けばよいのです。


 さて、今回は「繰り返し」の話をします。ただ、その前に「条件」の復習をしましょう。これは入門7の最後に書きましたが、if文のカッコの中にある「a > b」のような式のことで、次のようなものでした。

    a > b    aがbより大
    a >= b   aがbより大かまたは等しい
    a < b    aがbより小
    a <= b   aがbより小かまたは等しい
    a == b   aとbが等しい //「=」がふたつあることに注意
    a != b   aとbが等しくない

 これを使って例えばif(a <= b)などと書くと、これは「aがbより小さいかまたはaとbが等しいならば」という意味になります。if(a != b)なら「aとbが等しくないならば」です。

 この「条件」を使ってwhile文というものは次のように書かれます。

    while(条件){
        処理
    }

その意味は「条件が正しい限り中カッコのなかの処理を実行せよ」です。下に例を挙げます。

//while_sample.cpp
#include <iostream>
using namespace std;

int main()
{
    int i;
    i = 0;
    while(i < 100){
        cout << i << "  "; //見やすさのため数字の後に空白をいれた
        i++;                   //iを1増やせという意味です。(下を参照)
    }
}

このプログラムを実行すると0から99までの数字が画面に出力されて終わります。

Fig.1 while_sample.exeの実行画面

 while_sample.cppの説明は次のようになります。
 まず整数を入れる変数iが定義され、iに0が代入されます。それから、while文になるわけですが、while文では最初に条件がチェックされます。0は100より小さいので、whileの中の条件「i < 100」が成立し、whileの中カッコの処理が実行されます。
 whileの中カッコの中にはcout文がありますね。これが実行されるとiが画面に出力されるわけですが、それはつまり画面に0と出力されるということです。その後、 「i++;」というものがあります。これは、「iを1増やせ」という意味になるのです。(ちなみに、「i--;」とすると、「iを1減らせ」になります。)ここで覚えてください。これで、iが1増えて1になります。
 そして、また、whileの条件「i < 100」が調べられますが、当然、1は100より小さいので条件が成立し、whileの中カッコの処理が実行されます。それは、画面に「1」と出力し、また、「i++;」でiを1増やし、iは2になり、...と続いていくのです。
 iが99になって出力された後に「i++;」で1増やされて100となると、「i < 100」の条件が成立しなくなるので、whileの処理は終了します。ちなみにこのような繰り返しをループといい、ループが終わることをループをぬけるなどとも言います。上の例では「iが100になると(プログラムの実行が)ループをぬける」などと言います。
 このようなループはプログラミングでは必須なので自分でもいろいろ実験してください。

 ところで、C++では

    int i;
    i = 0;

を簡単に

    int i = 0;  //「iという整数変数を使うがその値をはじめに0にする」という意味

と書くことができます。以下では、このような書き方も使います。

 ループを実現するには、for文というものもあります。たとえば、while_sample.cppと同じ動作をするプログラムを、for文を使って次のように書けます。

//for_sample.cpp
#include <iostream>
using namespace std;

int main()
{
    int i;
    for(i = 0; i < 100; i++){
        cout << i << "  ";
    }
}

 forの丸カッコの中は「はじめにiを0として、iが100より小さい間は中カッコの中の処理を実行する。ただし、処理を実行するたびにiを1増やす」という意味になります。これはあれこれ考えるよりこのままの形で覚えてください。例えば、「愛してる」と100回繰り返すプログラムはどうすればよいでしょう。それは、次のようになります。

//for_sample2.cpp
#include <iostream>
using namespace std;

int main()
{
    int i;
    for(i = 0; i < 100; i++){
        cout << "愛してる ";
    }
}

Fig.2 for_sample2.exeの実行画面(もういいよって感じ?)

 for文の処理内でiは明示的には使っていませんが、見えないメモリの中では0から99まで動いていくのです。その間にcoutの文が実行されるので、「愛してる」が100回繰り返されるわけです。

 なお、「iの定義」は、for文の丸カッコの最初のところですることもできます。つまり、

for(int i = 0; i < 100; i++){
    cout << "愛してる ";
}

のようにfor文を書くこともできるのです。この場合、iはfor文内のみで有効な変数になります。

 それでは、for_sample2.cppと同じ動作をするプログラムをwhileを使って書いてみてください。それは、次のようになりますね。

//while_sample2.cpp
#include <iostream>
using namespace std;

int main()
{
    int i = 0;
    while(i < 100){
        cout << "愛してる ";
        i++;
    }
}

 なぜ、同じことをするのに違うやり方があるのでしょうか?それは、多分、歴史的な理由と、状況によって微妙に使いやすさが異なるという理由のためでしょう。はじめの内は人(本)が使っているのを見てまねていればよいのだと思います。

 ところで、もし、次のようなプログラムをコンパイルして実行したらどうなるでしょう。

//エラー
#include <iostream>
using namespace std;

int main()
{
    int i = 0;
    while(i < 100){
        cout << "愛してる ";
    }
}

 これは無限ループとよばれるもので、プログラムとしては間違いですが、コンパイルできてしまい、しかも、実行もできます。そして、ループからぬけられなくなって、コンピュータは永遠に愛をささやき続けるのです。(Ctrl + Cや3本の指を使う強制終了などをすれば普通はとまります。いきまり電源を切るようなことはしないでくださいね。やりかたを知らない人は経験者に聞いてください。ただ、3本指の強制終了で画面が動かなくなることもあります。こういう場合は大変です。したがってこういうことはなるべくしない方がよいでしょう。)
 なぜでしょうか。それは、whileの処理でiの値を変えていないので(「i++;」がない)、iはずっと0のままであり、したがってwhileの条件が永遠に成立するからです。このような無限ループは困りものです。

 しかし、以下のプログラムをみてください。

//input100.cpp
#include <iostream>
using namespace std;

int main()
{
    int i = 0;
    while(i != 100){
        cout << "好きな数字を入力してください。" <<endl;
        cin >> i;
    }
    cout << "終了しました。" <<endl;
}

 これも実行してみると無限ループのような気がしてしまいます。というのは数字を入力してみるとすぐ「好きな数字を入力してください。」と出てやりなしになるからです。しかし、これは、実は100と入力すればループからぬけられるのです。

Fig.3 input100.exeの実行画面

 whileの丸カッコの中には「!=」がありますね。この意味は「iが100に等しくない限りループを続ける」なのです。もう一度上に書いた「条件」のところを見て考えてみてください。

 実は、上のような仕組みをわざと無限ループを使って実現することもあります。それには、while(1)(または、for(;;))というものを使います。こう書くと無限ループになるのです。
 そして、無限ループであっても、そうではない普通のループであっても、ループの中で「break;」という命令を実行するとループからぬける(ループを終了する)ということになっています。これを使うとinput100.cppと同じ動作をするプログラムを次のように書くことができるのです。

//mugenloop.cpp
#include <iostream>
using namespace std;

int main()
{
    int i = 0;
    while(1){
        cout << "好きな数字を入力してください。" <<endl;
        cin >> i;
        if(i == 100){
            break;
        }
    }
    cout << "終了しました。" <<endl;
}

 一応説明しましょう。まず、iの使われ方が前と違うことに注意してください。これはwhileの「条件」とは関係ありません。というかwhile(1)の丸カッコに「条件」はなく、いつも「続行」を意味する「1」があるのです。ループの中の「好きな数字を入力してください。」の後では、入力された数字がiに格納されます。そして、もし、iが100に等しければ、「break;」が実行されループをぬけて終了するようになっているのです。もちろん、iが100以外ならループは続行です。

 このような無限ループはいろいろなところで使えます。たとえば、「大魔王とヒーローの対決ゲーム」で、対決の決着がつくまでゲームを続けるようにすることもできます。しかし、そのためにはもう一つ準備がいるので、次回はその話をしましょう。

 そのかわり、ここでは、練習問題をひとつ出します。正解がでるまで同じ質問を繰り返すプログラムを書いてください。...答えは、次のようになるでしょう。

//quiz.cpp
#include <iostream>
using namespace std;

int main()
{
    int i;
    while(1){
        cout << "日本一高い山はどれですか。" << endl;
        cout << "数字を入力してください。" << endl;
        cout << "1.ヒマラヤ 2.富士山 3.ロッキー" << endl;
        cin >> i;
        if(i == 2){
            break;
        }
        cout << "はずれです。" << endl;  //不正解の場合breakされないので
                                                     //この文が実行される。
    }
    //ループ終了後次の文が実行される。
    cout<<"正解なので終了しました。"<<endl;
}

Fig.4 quiz.exeの実行画面

 この手のプログラムの基本形ですので、よく考えてみてください。


おまけ
 今回はループの勉強に追われてクラスを使ったプログラムができませんでした。それで、一応おまけをつけておきます。講義の進行には関係ないので、読まなくても大丈夫です。元気の残っている人だけどうぞ。

 それでは、前回のおみくじクラスを使って「7日(1週間)分のうらないを一度にやる」プログラムを書いてみます。

//omikuji3.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

class Omikuji
{
    int un;  //ラッキーナンバー
public:
    Omikuji();   //コンストラクタ
    void hiku();
};

//コンストラクタ、ラッキーナンバーをユーザに代入してもらいます
Omikuji::Omikuji()
{
    srand( (unsigned)time( NULL ) );
    cout<<"自分のラッキーナンバー(1〜5)を入力してください。"<<endl;
    cin>>un;  //自分のラッキーナンバーを代入
}

//おみくじを引く
void Omikuji::hiku()
{
    int x;
    x = rand() % 5 + 1;   //1〜5の乱数を発生させ、xに代入、これが引いたおみくじの番号
    cout << "あなたの運勢は";
    if(x == un){                                            //xとunが等しければ大吉
        cout << "大吉ということです。" << endl;
    }
    else{                                                    //xとunが等しくなければ「普通」
        cout<<"並みということです。"<<endl;
    }
}

int main()
{
    Omikuji ok;  //おみくじ
    cout << "1週間分の占いです。" << endl;
    for(int i = 0; i < 7; i++){
        cout << "今日から" << i << "日後:" << endl;
        ok.hiku();
    }
}

前回のomikuji.cppから書き換えたところは、mainの中だけです。

Fig.5 omikuji3.exeの実行画面

 残念ながら表示がちょっとさえないですね。これをよくするには、Omikujiを少々書き直す必要が出てきますが、それは「わずか」であるはずです。


目次のページ
前のページ
次のページ