2000/10/18 加筆修正
修正部分はこの部分と同色で示す
ループが大きくなりすぎて、分かりにくくなっていたため、
最後のじゃんけんプログラムのループの始まりと終わりなどに
説明のコメントを加える
長すぎるソースは関数に分割すればさらに読みやすくなるため、
分割した方がよいかとも思われるが、一度に行うと講座の内容が
煩雑になるため、割愛
第7回:『繰り返し』あいこでジャンケンはおわらない!
・乱数
さて、前回(遠い昔のことですね(苦笑))は乱数をつかって本当のジャンケンのようにしてみました。
今回は本当のじゃんけんと同じように、あいこでは終わらないようにしましょう。
・繰り返し 〜while文〜 (ホワイル文)
……と言う感じでいままで講座を進めてきたのですが、なんだか面白く無くなってきたので(笑)
今回から、講座の形を変えてみようと思います。
ある本を読んで、難しい事を難しい言葉で説明することの馬鹿らしさと、読む側に与える悪影響を知りまして…。
んなわけで、今回から講座の内容を『漫才形式』にしたいと思います。
登場する漫才師はベタいのですが『女の子』と『変な生物』です。とはいってもオタクに走らないように
留意するつもりですので、安心下さい(笑)
『二人のへたくそな漫才を聞いているうちに、なぜかC言語が使えるようになった』
ってのを狙って行くつもりです。
とりあえず、今回はテストってことでやってみます。
評判が悪いようなら、もとの形式に戻すので、意見をくださいね〜。
掲示板でも、メールでもどっちでもOKなので。 ではでは。
・はじまりはいつも突然 〜漫才C結成〜
黄色い変な生物:はいっ。ど〜もど〜も。漫才師”漫才C”の変な生物です〜。
女の子 :はい。ど〜もど〜も……ってなんであたし達が漫才師なのよ!
黄色 :いや、管理者の設定で…
女の子:何いってんのよ。あんた管理者にだまされてるわよ。名前だって”黄色”に省略されてるし。
:タイトルもめちゃくちゃだし。
黄色 :ぐはっ! なんでそんな省略の仕方やねん!
女の子:まぁ、省略しなくても後ろに付くのが”変な生物”だからどっちでも良いような気もするけど。
むしろ、省略した方がいい気も…
黄 :なんでやねん!黄色は酷いわ。いくら名前が未定やからって……
んなこと言ってる間に、”黄”になってるーーーーーーーーーー!!!
C子:あはは。ま、どっちでもいいんじゃない?
黄 :んなこと言うとる間に、あんさんも名前が”C子”になっとるでー
C子:っなに!
なによこのださださな名前はっ!!
黄 :……C子はクラスの概念を覚えると、C++子になるのであった。ふむふむ。
C子:なに読んでんのよ!あんた!!
黄 :『楽しく!できる!!C講座 設定資料集』
C子:あ、頭が痛くなってきた。
黄 :管理者は「オタクに走らない」とか言っときながら、すでにオタクに走ってるって意見も…。
C子:……ほんとに。
黄 :ま、えっか。わしらの知ったこっちゃないな。
とりあえず、今回の講座をやっていきまひょ。
・繰り返し 〜while文〜 (ホワイル文)
黄 :英語でwhileとはどう言う意味かしっとるかね?C子君。
C子:なんでアンタがえらそうなのよ。……まぁいっか。
whileは「〜する間」って意味でしょ?
黄 :なにっ!?
C子:ちなみに発音は「ホワイル」より「ワイル」って感じかしら。
なに驚いてるのよ。そんなの中学卒業してたら知ってるわよ。
黄 :いや、てっきり「白い…?」とか言うのかとおもっとったのに…。
C子:なによ。で、アンタが「それはホワイトやろ!」とかって突っ込むつもりだったの?
ばっかじゃない?今日日の漫才師はそんなべたべたなボケはしないのよ。
黄 :ふっ。
C子:なによ。
黄 :とうとう自分が漫才師だと認めたな!!
C子:げっ!
黄 :ははははは。やっぱり体の中を流れる血にはかてんと言うことだよ。
C子:い、いや、アレはモノのはずみというか…。あんたに合わせたのよ!
黄 :何を言っても見苦しいな。血は水より濃いのだよ。ははははははは。
C子:もう、どーでも良くなってきた…。
黄 :さて、話をもどすで。
C子:切り替えはやいわね。ま、良いけど。
黄 :とりあえず、whileの意味は、そのとおり「〜する間」
つまり、while文は「〜が成立している間は繰り返す」という仕組みになっとるわけ。
C子:要するに、「〜してる間はグルグル回る」ってわけね。
黄 :簡単に言うとそんなとこやね。
とりあえず、使い方を見てもらいまひょ。
| 例8.1
|
:
int i=0;
while( i<=10 )
{
printf("今日から私も漫才師\n");
}
:
|
|
黄 :とすると、どうなるか分かる?
C子:いやな例文ね…(汗)
たぶん……whileの意味からすると、iが10以下の間「今日から私は漫才師」って画面に出力されるんでしょ。
黄 :おおおっ!さすがやね。
C子:最初にwhileの意味を説明させたんだから、それぐらいわかるわよ。
黄 :つまり、このwhile文(ホワイル文)の書式はこうや。
| while文の書式
|
:
while( 条件式 )
{
実行内容
}
:
|
|
黄 :条件式が成立している間、実行内容が繰り返し実行されるわけやね。
whileやから、”成立している間”っていうわけや。
黄 :じゃ、この例文のマズいところは?
C子:へっ?
黄 :この例のヤバイところ。
… ”printfで出力される文がヤバイ”ってのはナシで。
C子:ちっ
黄 :”ちっ”ってなんやねん!(笑)
ま、今回初めて出てくる構文やからね。しゃーないわ。
例文を良く見てみ。どんなときにグルグル回るんやった?
C子:だから、”iが10以下のときだって…。
黄 :じゃ、iが10より大きくなるのはいつ?
C子:えーと。……あれ?
黄 :そのとおり。この例は、「iが10より大きくならないので永遠に繰り返す」ことになんねん。
つまり、『無限ループ』っちゅーやっちゃ。
この例の場合、「今日から私は漫才師」っていう文が永遠に画面にあわられ続けるっちゅーわけや。
C子:宇宙一根性のひん曲がったプログラムね。
黄 :宇宙一て…(笑)
ま、とりあえず、今回扱うwhile文っちゅーのはそれだけ『簡単にマシンを暴走させる危険がある文』や。
っちゅーこっちゃね。
とはいっても、恐れることはあらへん。この程度の暴走は、DOS/V機ならウィンドウの右上の×ボタンをクリックすれば止められるし、最悪でも『Ctrl + Alt + Del』で止められるから。
C子:「この程度の暴走」って…
黄 :いや、パソコンを強制終了させないとあかんプログラムかて、そのうち簡単にできるから…。
C子:せんでよろしい!
黄 :まぁ、暴走する例だけしか挙げへん。っちゅーのもアレやから、修正版を。
| 例8.2
|
:
int i=0;
while( i<=10 )
{
printf("今日から私も漫才師\n");
i = i+1;
}
:
|
|
黄 :こうしてやれば、一回画面に文が出力される度にiが1増えるから、全部で11の文が表示されるわけやね。
C子:11?10じゃないの?
黄 :while文はwhile()のところで、続けるかどうかを判定するんや。
そやから、1回目は0。 2回目は1。 ……10回目は9。 11回目でようやく10になって、12回目は11やから…
C子:実行されない。
黄 :っちゅーわけやね。
C子:なにか騙されてるみたい。
黄 :最初は、ここらへんの感覚がよう分からんと思うわ。
右手で文を抑えながら、左手で数えていったらええと思うで。まぁ、「1からじゃなくて、0から数え始めてるから。」ってのが理由なんやけど、普段の生活で0から数え始める。なんてことなかなかないからなぁ。
C子:…1回目。0。……2回目。1。
黄 :どうや?
C子:確かにそうなってるみたいだけど。
黄 :まぁ、修正した例でも”あまり良くない例”やからなぁ。
C子:なぜ?
黄 :このプログラムで実際に画面に表示されるのは11回やろ?
そやけど、このソースにはどこにも11っちゅー数字がでてきとれへん。
C子:なるほど。
黄 :出てくるのは10やから、ぱっと見たとき10回表示されるんちゃうかな?と期待してまうんや。
C子:たしかに。
黄 :そやから、ホントにええ例はこうや。
| 例8.3
|
:
int i=0;
while( i<11 )
{
printf("今日から私も漫才師\n");
i = i+1;
}
:
|
|
黄 :こうすれば、見た目にも11回まわりそうやし、実際11回まわる。
C子:11にしたら12回まわるんじゃない?
黄 :そやから、『<=』を『<』にしとるんや。
C子:うーん…。
黄 :簡単に”『<=』を『<』”ていうように”=を抜くと1回まわる回数が減る”ってのを感覚的に知っておく。
ってのも大事やと思うで。
C子:”=”のときの分が減るから、1個減るんだよね。うーん。
黄 :ま、ようけ悩んだって(笑) <管注:まぁ、たくさんなやんであげて。>
黄 :ほかには、こうするんもアリやけどな。
| 例8.4
|
:
int i=1;
while( i<=11 )
{
printf("今日から私も漫才師\n");
i = i+1;
}
:
|
|
黄 :こうしても、11回まわりそうやし、実際11回まわる。
C子:今度はどこを変えたの?
黄 :iの最初の値を1にしたんやね。今までは0やったけど。
C子:ちょっとまってよ。さっきのソースを見るから…。
確かに、そこが変わってるわね。
黄 :これをまた、さっきの右手で押さえながら…ってやってくと11回まわるで。
C子:ほんとだ…。
黄 :つまるところ上の2つならどちらも良い例やねん。
でも、実際に繰り返し文を使うときは、”最初の値を0から始める場合が多い”から上の例を使うことが多いわ。正確には多い、というより、そっちに慣れた方が良いってとこやけど。
C子:なんで?
黄 :そのうち分かる。
C子:なんで?なんで?
黄 :そのうち分かる。
C子:なんで?なんで?なんで?なんで?なんで?なんで???????
黄 :だっーーーーーー!
分かった。んじゃ、説明しよか?
C子:説明してして。
黄 :今はまだ説明せーへん方がええと思うけどなぁ。
C子:説明♪説明♪
黄 :はぁ。
繰り返しってのは配列に対してよく使われるねん。
C子:はいれつ?
黄 :配列っちゅーのは、添え字を持った変数の並びで、例としてよく箱が並んでる姿が……
C子:………。
C子:ぐぅぐぅぐぅ。
黄 :…………。
黄 :……。
黄 :(ぷちっ)
黄 :寝るなーーーーーーーーーーーーーーー!!
C子:ほぇ?
黄 :あんさんが説明しろ。っちゅーから説明しとんのに!!
C子:なんか難しくなりそうだったから…。
てへっ♪
黄 :「てへっ♪」じゃない!!
まーええわ。
また、配列はそのうち説明するから。
C子:だから”変ちゃん”だーい好き。
黄 :変なあだ名つけるな。バカ。
C子:ちぇ。
黄 :何を説明しとったんか忘れるわ。
黄 :ま、今の時点では、上の方がよう使われるんやなー。ぐらいでOKや。
C子:上の方がよう使われるんやなー。
黄 :バカ言われたからってすねるな。
先行くで。
C子:ふんだ。
C子:せんせー
黄 :なんや。
C子:なんでiを1増やすところは、「i+1」とだけ書かないんですかー
黄 :ええ質問やね。
C子:へへへ。
黄 :(ちょっと褒めたらこれや。)
C言語では、変数の値は”基本的には代入以外では変化せーへん”のや。
C子:ふぅん。
代入は”=”だよね?
黄 :そのとおり。
やから「i+1」と書いてもiの値には全く影響はない。
そこで、「i = i+1」と書いてやると…
C子:iに1を足した値がiに代入されて、1増える…。
黄 :完璧やな。
C子:へへへへへ。
黄 :あと「i = i+1」ってのはこんな風にも書かれる。
| インクリメント
|
:
i= i+1;
i++;
/* 上の2つは全く同じ意味 */
:
|
|
黄 :コメント文(赤い部分)にも書いたとおり、2つは全く同じ意味になるんや。
(注: n = (i + i++); というような書き方をすると動作は不定です)
( n = (i + i+1); と書きましょう)
C子:へぇ。
黄 :下の方が楽やろ。
こっちの方が繰り返しでは良く使われるんや。下のように「なんたら ++」とやって値を
1増やすことを”インクリメント”って呼ぶんや。
覚えとき。
C子:へい。おやビン。
黄 :だれがおやビンやねん。まぁ、ええわ
このインクリメントを使えば 例8.3 は次のように書き換えられるんや。
| 例8.5
|
:
int i=0;
while( i<11 )
{
printf("今日から私も漫才師\n");
i++;
}
:
|
|
黄 :インクリメントが使えるところ、つまり1を加えるだけの所は普通インクリメントを使うのが普通やね。
・ while文を使って…
黄 : それではお待ちかね今回のソースや。
今回の目標は「while文(繰り返し文)を使って"あいこ"を実現しよう」ってとこやな。
黄 : まぁ、ソースを見せる前に最後にもう一回だけ確認しとこ。
C子: さっき、「ソースや。」って言ったじゃない。
黄 : ちょっと待っとき。今回新しくでてきたことを、C子は全部言えるんか?
C子: う゛っ…。
黄 : どうや?え?ええ?
C子: 私がわるぅ御座いました。おやびん。
黄 : おやびんはええっちゅーねん。
んなわけで軽く復習や。
| while文の書式
|
:
while( 条件式 )
{
実行内容
}
:
|
|
黄 :ここでどんなときに実行内容が繰り返し実行されるんやった?
C子:whileが〜する間って意味だから、条件式が成立している間実行されるのよね。
黄 :すごいやんか。やるねぇ。
C子:ふふん。まかせなさい!
黄 :(おだてといたら後が楽そうやな)
C子:なんか言った?
黄 :いやいや。あと、今回は使わへんけど…
| インクリメント
|
:
i= i+1;
i++;
/* 上の2つは全く同じ意味 */
:
|
|
黄 :コレも覚えときや。
(しつこいですが… n = (i + i++); というような書き方をすると
動作は不定です)
( n = (i + i+1); と書きましょう)
・今回のソース
a)じゃんけんの分析
黄 :今回の変更は”じゃんけんのあいこ”を実現するんやったやろ?
C子:そうね。
黄 :そのための今までの説明やったわけやけど、どうやって実現するか分かるか?
C子:うーん。そういえば…。
黄 :まぁ、そのためにはじゃんけんを分析したらええ。
C子:じゃんけんを分析?
黄 :そうや。例えば今回は”じゃんけんのあいこ と それ以外 の違い”を分析する。
どこが違う?
C子:そんなこと突然言われても…。
うーん。あいこの時とそれ以外の時…。
黄 :じゃんけんほい!
C子:何よ、突然。
黄 :じゃんけんほい!(グー)
C子:ほい!(パー)
やったー勝った!勝ったー!!
黄 :ちゃうやろバカ。
C子:ば、バカ。
黄 :じゃんけんを分析するためのじゃんけんやろ!
C子:……バカバカバカバカバカ。
黄 :ほい。いくで。じゃんけん…
C子:バカって言った。バカって言った。バカって言った…
黄 :だぁぁぁぁ!わかった。悪かった!悪かったから。
C子:C子様天才、最高。って言ってくれなきゃヤダ。
黄 :調子にのるな!
C子:てへ☆
黄 :てへ☆じゃない!
いくで。じゃんけん…
C子:ほい!
黄 :あいこで…
C子:しょ!
黄 :あいこで…
C子:しょ!
黄 :あいこで…
C子:しょ!
黄 :あいこで…
C子:しょ!
黄 :あいこで…
C子:しょ!
:
:
黄 :あいこで…
C子:しょ!
黄 :あいこで…
C子:しょ!
C子:勝った!勝ったわ〜!!98回目にしてやっと勝ったわ!!
黄 :ゼェゼェ…
これで分かったやろ…
C子:何が?
黄 :もういい。
黄 :つまるところ、あいこの間はずっと終わらへんっちゅーこっちゃ。
C子:そんなの、当たり前じゃない。
黄 :お、お前……
C子:ねぇ、怒った?
黄 :っちゅーわけで、実現の方法としては『あいこじゃなければ終わる』というようにするわ。
(C子を無視)
C子:ねぇねぇ、怒った?
黄 :ここでつまり、”あいこじゃなければ条件式が成立しなくなるように”ソースを書くんや。
(C子を完全無視)
C子:ねぇねぇねぇねぇねぇねぇねぇ、怒った?
黄 :だーーーーーーーーーーーーー!うるさいわ!!
そりゃ怒るっちゅーねん!!
C子:”変ちゃん”だーい好き。
黄 :そのキラキラ光線やめて。
C子:(キラキラキラ)
黄 :きしょいから。
<管注: きしょい = 気色悪い>
b)まとめ
(ボロ雑巾のようになりながら、黄 登場。)
黄 :以上のことをまとめて表にしてみたわ。
| じゃんけん分析のまとめ
|
・じゃんけんはあいこでは終わらない。
・つまり、あいこのときは繰り返せば良い。
・すなわち、while文の条件式は”あいこである”こと。
|
|
C子:ソースはこんな感じです☆
赤色の部分が変更したところなのでよろしくね☆
黄 :わいの決めゼリフを…
C子:なんか言った?
黄 :なんでもありません。
c)ソース
/*Janken あいこのときは終わらない本物のじゃんけんゲームになった */
#include <stdio.h>
#include <stdlib.h> /* 乱数関数 srand(), rand() を使うために必要 */
#include <time.h> /* 時間を求めるtime()を使うために必要 */
void main(void)
{
char selected; /* プレイヤーが選択したものを入れておく */
int com; /* コンピュータの出す手を入れておく */
int is_finish=0; /* じゃんけんが終わったどうかを示す変数(フラグ)
is_finish = 0 → あいこ・試合続行
is_finish = 1 → 決着ついた・プログラム終了 */
printf("************************************** \n");
printf("* じゃんけんゲーム * \n");
printf("************************************** \n");
printf("\n");
while( is_finish == 0 ) /* メインループの始め */
{
printf("#### 説明 ########\n");
printf("# g : グー #\n");
printf("# t : チョキ #\n");
printf("# p : パー #\n");
printf("#################\n");
printf("\n");
printf("\n");
printf("ジャンケンしよう!\n");
printf("ジャンケン……\n");
scanf("%c",&selected); /* プレイヤーが選択したキャラを
selected に格納 */
printf("ほいっ!!\n");
srand((unsigned)time(NULL)); /* 乱数ジェネレーターの初期化 */
com = rand()%3+1; /* コンピュータの出す手を
1から3で決める */
/* com の値を出すキャラクターに置き換える */
switch(com){
case 1:
com = 'g';
break;
case 2:
com = 't';
break;
case 3:
com = 'p';
break;
}
switch(selected){ /* 2つ目switch文の始め */
case 'g':
if(selected == com){
printf("++++++++++++++\n");
printf(" あなた:グー\n");
printf(" わたし:グー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あいこ! #\n");
printf("\n");
is_finish = 0; /*省略可能*/
}
else if(com=='t'){
printf("++++++++++++++\n");
printf(" あなた:グー\n");
printf(" わたし:チョキ\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あなたの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
else if(com=='p'){
printf("++++++++++++++\n");
printf(" あなた:グー\n");
printf(" わたし:パー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# わたしの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
break;
case 't':
if(selected == com){
printf("++++++++++++++\n");
printf(" あなた:チョキ\n");
printf(" わたし:チョキ\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あいこ! #\n");
printf("\n");
is_finish = 0; /*省略可能*/
}
else if(com=='g'){
printf("++++++++++++++\n");
printf(" あなた:チョキ\n");
printf(" わたし:グー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# わたしの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
else if(com=='p'){
printf("++++++++++++++\n");
printf(" あなた:チョキ\n");
printf(" わたし:パー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あなたの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
break;
case 'p':
if(selected == com){
printf("++++++++++++++\n");
printf(" あなた:パー\n");
printf(" わたし:パー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あいこ! #\n");
printf("\n");
is_finish = 0; /*省略可能*/
}
else if(com=='t'){
printf("++++++++++++++\n");
printf(" あなた:パー\n");
printf(" わたし:チョキ\n");
printf("++++++++++++++\n");
printf("\n");
printf("# わたしの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
else if(com=='g'){
printf("++++++++++++++\n");
printf(" あなた:パー\n");
printf(" わたし:グー\n");
printf("++++++++++++++\n");
printf("\n");
printf("# あなたの勝ち #\n");
printf("\n");
is_finish = 1; /*決着がついた*/
}
break;
default:
printf("エラー!!");
printf("g,t,p の中から選択してください!!\n");
break;
} /* 2つ目switch文の終わり */
} /* メインループの終わり */
} /* main文の終わり */
|
C子:さて、どうでした?できましたか??
黄 :お前が聞くなよ…。
C子:なんで、これって省略可能って部分があるの?
黄 :それは、is_finish には最初に0が代入されているので、さらに0を代入しても意味が無い。ってことや。
C子:0の上に0を入れても意味がないって事ね。
黄 :そうや。
ただ、今回はプログラムの可読性を高める為に一応書いてある。
こういう風に一対になっていれば、一方の動作から他方の動作を推測できるのでプログラムが分かり易いプログラムになるわけや。
・講評
黄 :以上で今回の講座は終了…やね。
C子:いろいろあったけど、楽しかったね。
黄 :まぁ…な。
C子:なによ。あまり乗り気じゃないのね。
黄 :ちょっと疲れた。
C子:コレだから”じじー”は。
黄 :(ぷちっ)
誰が”じじー”じゃ!こらぁぁ!!
C子:きゃ〜
(C子走り去る)
(黄も追いかけて走り去る)
(突然C子が現れて)
C子:さて、今回の講座はどうでした?
黄 :楽しかったかな?
C子:どこから沸いてきたのよ。
黄 :まぁ、鬱陶しく感じられなければ、わいは満足やけどなぁ。
C子:でも、管理人は疲れたらしいわよ(苦笑)
黄 :なんせ、4時間ぐらいぶっ続けて書いてるもんなぁ。
C子:それも、24時過ぎから。
黄 :あほやな。
C子:うん。あほや。
黄 :また、次回会うことがあればお目にかかりましょう。
C子:というか、絶対会いましょう。
なにがあっても、出現します!
黄 :なんでも、「鬱陶しい」っていう意見があれば止めるかもしれないらしい。
C子:うー。
黄 :うー。
黄・C子:「鬱陶しい」ってメールは止めてね☆
黄 :それでは
C子:また次の機会に
黄・C子:お目にかかりましょうー
黄 :ではでは〜
C子:じゃぁねぇ〜
なにやら、漫才だか小話だかよく分からない形になってしまった…(苦笑)
なにはともあれ、今回の講座でじゃんけんゲームもついに本格的なものになりました。
プログラミングの柱のうち、「while文によるループ」も理解できたのではないでしょうか。
言葉が堅くなるとさらに難しく感じます。
これから、さらに難しい(ややこしい)内容をやっていく上では、このぐらいくだけているほうがいいのかもしれません。
なにはともあれ、感想をお待ちしております。BBS・メールでお願いします。
次回は
配列
について説明します。
今回も少し、話題にのぼっていましたね。C子は寝てましたが(苦笑)
これは、繰り返し文と有効に使うとかなり便利なものです。詳しくは、次回に…