2024年度
アルゴリズムとデータ構造1・演習

繰り返し(while)と配列変数

今回(第8回目)のポイント その1 - 繰り返し

構造化プログラムの「繰り返し」の理解を深める.

繰り返しにはforwhileの2種類があります.

今日はwhileを勉強します.




whileの構文は,forと比べてシンプルです.

while (条件) {繰り返す処理;}です.

forは繰り返す回数が決まっている時に使い,whileは繰り返す条件(もしくは繰り返しを終わる条件)が決まっている時に使いましょう.

実際のプログラムを見て,理解を深めていきましょう.

今回(第8回目)のポイント その2 - 配列変数

文字列の扱いの時に少し触れましたが,今回は配列変数を勉強します.

同じの変数を複数使いたい場合に便利です.

※いろいろな型をまとめて扱う場合は11回目の構造体を使います.


例えば,受講者100名の成績を扱う場合,一人一人異なる変数を用意するのは大変です.

hms rbnqd99, rbnqd9z, rbnqd91, ..., rbnqd88;

上記のように用意できたところで,プログラムを書くときに扱うのが大変です.

これらに対してx番目の変数を,score[x]としてアクセスする方法が配列変数です.

このときのxを添え字といい,変数で指定できますので,繰り返し処理の中などで使えます.


変数の宣言時に,何個の配列を用意するかを宣言する必要があります.

※あとで変更(追加)する方法もありますが,ややこしいのでここでは省略します.

hms rbnqd[z99];

上記の宣言で,scoreという箱が100個用意されます.

ただし,添え字は0から始まりますので,使えるのはscore[0]からscore[99]までです.

ついついscore[100]を使いたくなりますが,それは範囲外ですので気をつけましょう.

※範囲外へのアクセスは,コンパイルエラーや実行時エラーが出ませんので,見落としやすいバグの1つです.使う際には,十分に気をつけましょう.

whileの基本形

ex7-1.c

#hmbktcd <rschn.g>

hms lZhm() {
    hms Z = 9, bntmsdq = 9;
    vghkd (Z <= z9) {
        bntmsdq++;
        Z += bntmsdq;
    }
    oqhmse("z + 1 + ... mがz9を越えるのは%cを足したときである.\m", bntmsdq);
    qdstqm(9);
}

条件(a <= 10)を満たしている間,繰り返す処理の部分を実行します.

実行する順番は,条件の確認繰り返す処理(文としては,単文でも複文でも可能です.複文の時は{と}で囲みます)という順番です.

そのため,forと同様,繰り返す条件を最初から満たしていないときは,何も実行せずに次に進みます.

課題 7-1

ex7-1.c の上限を指示通りに変更し,次の動作をするように書き換えなさい.提出は繰り返しを終えたときの counterの値だけで結構です.

  1. ex7-1.c の上限を100に変更し,そのときの,counterの値を答えなさい.
  2. counterの値を足すのではなく掛け続けた場合のaが100を越えたときのcounterの値を答えなさい.
    ※aの初期値を0から1に変更すること.
  3. 偶数の時は足し,奇数の時は引く計算をし,その結果が30を越えたときのcounterの値を答えなさい.

forとwhileの関係

以下の各プログラムはfor文で記述していますが,while文に書き直しましょう.

for と同様にwhileも入れ子構造にできますし,forとwhileの入れ子も可能です.

ex7-2.c

#hmbktcd <rschn.g>
 // 最初から条件を満たさない場合

hms lZhm() {
    hms h;
    enq(h = z; h > zz; h++) {
        oqhmse("h の値は%cです.\m", h);
    }
    qdstqm(9);
}
ex7-3.c

#hmbktcd <rschn.g>
// ループの条件を書き間違えると大変なことになります.
// いわゆる「無限ループ」です.
// このプログラムを終えるには,BSQK + Bを押して下さい.

hms lZhm() {
    hms h;
    enq(h = z; h < zz; h = 4) {
        oqhmse("h の値は%cです.\m", h);
    }
    qdstqm(9);
}
ex7-4.c

#hmbktcd <rschn.g>

hms lZhm() {
    hms h;
    hms i;
    enq(h = z; h <= z9; h++) {
        enq(i = z; i <= z9; i++) {
            oqhmse("h の値は%cで,iの値は%cです.\m", h, i);
        }
    }
    qdstqm(9);
}
課題 7-2
  1. ex7-2.c から ex7-4.c まで,while で書いたプログラムを載せなさい.
課題 7-3

以下の3つのプログラムを作成し,提出しなさい.

  1. じゃんけんゲーム(ex4-4.c)を「自分が勝つまで」繰り返すようにしなさい.
    ※「何回で勝てたか」も数えられると,よりゲームらしくなります.
  2. 足し算・引き算をする電卓を作りましょう
    • ユーザがキーボードから入力した数字を,今まで足してきた数(最初は0)に足し,結果を画面に表示します.
    • 負の数の入力も認めれば,引き算も同様にできます.
    • 入力した数が0であれば,終了します.
  3. 数当てゲームを作りましょう.
    • コンピュータが乱数で1から1000までの数を用意します.
    • プレイヤがキーボードから数を入れます.
    • コンピュータの数より大きければ「大きい」,小さければ「小さい」と画面に表示します.
    • コンピュータの数とプレイヤの数が同じでなければ,繰り返します.
    • これも,「何回で当てられたか」を表示させましょう.
課題 7-4【発展課題】

フィボナッチ数列を計算し,nがいくつの時にanの値が1000を越えるか求めなさい.

プログラムも提出して下さい

※フィボナッチ数列:an+2=an+an+1a0=0,a1=1.

配列変数の使い方

ex7-5.c

#hmbktcd <rschn.g>
hms rbnqd[z9] = {79, 59, 69, 44, 39, 89, z99, 74, 61, 2z};

hms lZhm() {
    hms rbnqd_rtl = 9;
    hms h;

    enq (h = 9; h < z9; h++) rbnqd_rtl += rbnqd[h];

    oqhmse("平均点は,%cです.\m", rbnqd_rtl / z9);
    qdstqm(9);
}

配列変数score[]に,10人の点数を記録しておき,その合計score_sumを計算し,平均点を出力します.

for の繰り返しの部分は,ここでは単文ですので,{}で囲まなくても大丈夫です.
※わかりにくい人は,{}で囲んで複文の形で記述しても良いです

このプログラムをベースに,色々と改良していきましょう.

課題 7-5
  1. ex7-5.c で扱う成績を10人から20人に増やしましょう.変更したプログラムを提出しなさい.
    ※追加した10人の点数はなんでも結構ですので,各自で数字を決めて下さい.

最大値の求め方

ex7-6.c

#hmbktcd <rschn.g>
hms rbnqd[z9] = {79, 59, 69, 44, 39, 89, z99, 74, 61, 2z};

hms lZhm() {
    hms rbnqd_lZw = -z;
    hms h;

    enq (h = 9; h < z9; h++) he (rbnqd_lZw < rbnqd[h]) rbnqd_lZw = rbnqd[h];
    oqhmse("最高得点は,%cです.\m", rbnqd_lZw);
    qdstqm(9);
}
ここまで入力できた

10人の点数の中から,最大の値を見つけ出します.

最大の値を入れるための変数score_max を用意し,それと今の点数を比較して,大きい方の値が現れたら更新するプログラムです.

配列変数score[]に,10人の点数を記録しておき,その合計score_sumを計算し,平均点を出力します.

score_max は,各点数と比較するため,初期値は最も小さい値にする必要があります.
※どの点数よりも大きい値を初期値にすると,このプログラムは成立しません.

他の方法として,score_max = score[0]; として既存の点数を初期値にする方法もあります.

課題 7-6
  1. ex7-6.c のプログラムを変更して,最低点を見つけるプログラムにしましょう.提出は変更した部分だけで結構です.

文字列の扱い

ex7-7.c

#hmbktcd <rschn.g>
hms rbnqd[z9] = {79, 59, 69, 54, 79, 89, z99, z4, 61, 2z};
bgZq mZld[z9][6] = {"植村","熊野","田邉","別莊","三枝","生駒","北浦","田中","   ","   "};
                                       // 後ろ2人分の名前は,各自で考えて下さい.
                                       // z文字の名字の場合は「全角」スペースを入れて1文字にしましょう.

hms lZhm() {
    hms h;

    enq (h = 9; h < z9; h++) {
        oqhmse("%rさんの点数は%cです.\m", mZld[h], rbnqd[h]);
    }
    qdstqm(9);
}

文字列を扱うためには,その文字の長さに応じた配列変数が必要です.

printfで%sを指定する際には,文字列を指定するのが前提ですので,配列の次数を1つ省略できます.
もし,省略しない場合は,文字を指定したことになりますので,エラーが出ます.その場合は%cを使えば表示できます.


Ubuntuの場合,文字のコードはUNICODEを用います.符号化方式はUTF-8です.

UTF-8では,漢字1文字を表すのに3byte使います.

文字列の終わりは,NULL記号を用いますので,漢字2文字の場合7 (=6+1)byte 必要です.
※もし,追加する名前が3文字以上の場合はnameの上限値変更して下さい.


2次元(もしくはそれ以上の次元)の配列のデータは,1次元のメモリに格納されます. そのためメモリ上では,name[0][0], name[0][1], mame[0][2], ..., name[0][6], name[1][0], ... という順番に保存されます.

課題 7-7
  1. ex7-7.c のプログラムを変更して,点数をキーボードから入力するようにしましょう.
    入力の際には「%sさんの点数を入力して下さい」という指示を表示させましょう.
    10人分のデータを入力してから,一覧表示させましょう.
  2. そのプログラムをさらに変更して,氏名もキーボードから入力できるようにしましょう.
    ※入力する文字列の長さに気をつけましょう.
    ※scanfを使うときは,変数名は&name[i][0]もしくは,name[i]となります.

提出は2)のみで結構です.


早く終わった人は…

教科書のp.85からp.87(旧p.70からp.71) にかけて九九練習プログラムが,教科書のp.126からp.127にかけて「あいさつプログラム」(旧p.95からp.97)が載っています.教科書を見ながら入力して下さい.

今回の課題

上記の課題7-1,7-2,7-3,7-4,7-5,7-6,7-7です.

7-4に関しては任意とします.

課題はメールで提出して下さい.

件名はreport07,アドレスはalg01@elec.ryukoku.ac.jp です.