2020/08/26

エド君と学ぶC講座4

今回の内容 do文 while文 for文



エド君「ちょっと日が空いたが今回は繰り返し文…つまりループさせる文についてだ。」
つまりソシャゲによくある周回作業みたいなもんか。
エド君「まあ…間違っちゃいねぇけど…今までは1回しかできなかったプログラムを数回繰り返すためにこれを使うぞ。まずはdo文からだ。前に奇数か偶数か判定するプログラムを作ったがまずはこれを1回の起動で数回できるように変える。」

#include<stdio.h>

int main(void) {
 int x;
 printf("エド君「数字を入力しろ。」");
 scanf("%d", &x);
 if (x % 2)

  printf("エド君「その数は奇数だ。」");
 else
  printf("エド君「その数は偶数だ。」");
 return 0;
}

エド君「前回のがこれだな。do文を使うときにはdoとwhileの間にループさせたいプログラムを入力する。大体こんな感じだな。」

#include<stdio.h>

int main(void) {
    int a;
    do {
        int x;
        printf("エド君「数字を入力しろ。」");
        scanf("%d", &x);
        if (x % 2)

            printf("エド君「その数は奇数だ。」\n");
        else
            printf("エド君「その数は偶数だ。」\n");
        printf("エド君「もう一度するなら0,やめるなら1を押せ。」");
        scanf("%d", &a);
    } while (a == 0);
    return 0;
}



おおすごいな。これでループできるのか…
そういやこのwhileってのはどういう条件になってるんだ?
エド君「これはこの条件に合う間はループさせるって感じの文だな。0が入力されてる間はループされる。あと今回ループを終わらせるときに1を入力させるように促してるが実際の所は0じゃないものなら終了はする。あとdo文には読み込む値の制限を設けることもできる。例えば…」
do{
scanf("%d",&a);
}while(a<0||a>2)

エド君「このようにすれば0,1,2以外の数字が入力されるともう一度入力するように促すことができる。3択でできるものならこんな感じのループをつくると良さそうだな。ここで1つ論理否定演算子についても話しておくか。前にa!=bという演算子を載せたが似たものに!aという演算子がある。これはaが0なら1をそうじゃないときに0を返す演算子だ。さっきのwhileのやつだが…こんな風に書き換えることもできる。」

!(a>=0&&a<=2)

エド君「この場合はaが0以上かつ2以下でないという条件になるな。こんな感じのものを活用したド・モルガンの定理というものがある。簡単に説明すればこんな感じだ。」

a&&b=!(!a||!b)
a||b=!(!a&&!b)

エド君「あと複合代入演算子とかの話もしておくか。複合代入演算子は+=などの2つの演算を行う演算子だ。」

b+=a

エド君「こうすることでbにaの値を加えることができる。これを使えばループで数字を複数入力してその合計を求めることもできるわけだ。まあ今のだとb=b+aと同じと思ってくれていい。あとは後置と前置の増減を行う演算子の話だが…ここでクイズだ。」

a=9のときの++aとaとa++

エド君「この場合どんな数が出力されると思うか?」
なんかプラスの位置が違うな。真ん中のはそのまま9が出るとして両方とも10になるんじゃないのか?
エド君「不正解だ。++aは10だがa++は9のまま出力される。」
そうなのか…
エド君「わかりにくい場合は1足してからaが出るかaを出してから1足すかという認識で覚えるといい。」
なるほど…
エド君「余談だがこれはそれぞれを並べずに出したときであってもし仮に…」

printf("%d,%d,%d",++a,a,a++);

エド君「こんな感じにするとすべての数が10になる。原理は分かるな?」
最初ので10になってあと二つは出力時に加算されないからか。
エド君「そういうことだ。先に増減させるものを前置演算子、後で増減するものを後置演算子と呼ぶ。ここらへんはプログラムを読みやすくするからちゃんと覚えておけ。」



エド君「さて次はwhile文だ。まあdo文に出てきたwhileと同じだがこっちは後ろにつけていたものを前に持ってくる。例としてカウントダウンを行うプログラムを作ってみるか。」



ちょっと気になったんだがそれならそもそもdo文っているのか?
エド君「基本的にはwhileでいけるがdo文の特徴として1回は実行されるという点がある。whileだと条件式次第ではそもそも実行されないということもあるからな。これは式の評価がどこのタイミングにあるかの違いだな。」

#include<stdio.h>

int main(void) {
    int a;
    printf("エド君「数字を入力しろ。」");
    scanf("%d", &a);
    while (a >= 0)
        printf("%d ", a--);
    printf("\n");
    return 0;
}

エド君「こんな感じで任意の数字から0までのカウントダウンができるようになる。」
もしかしてなんだがこれカウントアップのほうもできるのか?
エド君「ああ。任意の数字までカウントアップしたいという場合にはもう一つ変数を置いてそれが入力された数字になるまで増加させれば良い。」

i=0;
while(i<=a)
printf("%d",i++);

エド君「まあscanfの後ろをこんな感じにすればいけるな。そういえばprintfを今まで使っていたがputsputcharという関数もあるぞ。」
どこら辺が違うんだ?
エド君「putsを使えば自動で改行をしてくれる。例えばだが奇数偶数のプログラムの結果を表示するところはこっちでもいけるわけだ。あとprintfだと%を文字として出すには%%とする必要があったがputsは一個でいい。putcharは文字を1文字出力ができる。」

putchar('*');

エド君「こうすると*が出る。このとき間違えやすいのが”と’を混同してしまうことだ。」
printfとは違うのか…
エド君「ここら辺もうまく使い分けていけ。最後はfor文だ。これはちょっと方式が違ってきてforの中に最大3つの式を入れて条件をつけていく。さっきのカウントダウンをfor文にしてみるか。」

#include<stdio.h>

int main(void) {
    int a;
    int i;
    printf("エド君「数字を入力しろ。」");
    scanf("%d", &a);
    for(i=a;i>=0;i--)
        printf("%d ", i);
    printf("\n");
    return 0;
}


エド君「大体基本としてはこんなもんだ。このように前処理、制御式、後処理みたいな感じで分かれているがもし処理が必要ないなら省略することもできる。前処理を入れてわかりやすくするために変数iを使ったが効率よくするなら今回これはいらねぇ。こうすりゃいい。」

#include<stdio.h>

int main(void) {
    int a; 
    printf("エド君「数字を入力しろ。」");
    scanf("%d", &a);
    for(;a>=0;a--)
        printf("%d ", a);
    printf("\n");
    return 0;
}

エド君「何も入れずにセミコロンだけ入れれば省略ができる。」
ほかのとこの省略できるのか?
エド君「ああ。後処理でやることがないならそこも省略できる。制御するとこを省略すると無限ループと化すけどな。」
なんかfor文が一番使いやすそうに感じるな…あと無限ループってこわくね?
エド君「まあ実際使う機会もおおいからな。他にも(i=2;i<=n;i+=2)にすると偶数だけ並べるとかもできる。まあ規定回数までループさせるときに使いやすいのは強みだな。」

エド君「for文は組み合わせることもできる。いわゆる多重ループだな。図形とか作るときにも使えるテクニックだ。例えば長さを入力して長方形を作るプログラミングなら…」

#include<stdio.h>

int main(void) {
    int a,b;
    int i,j;
    printf("エド君「縦の長さを入力しろ。」");
    scanf("%d", &a);
    printf("エド君「横の長さを入力しろ。」");
    scanf("%d", &b);
    for (i = 1; i <= a; i++) {
        for (j = 1; j <= b; j++) {
            putchar('*');
        }

        printf("\n");
    }
    return 0;
}



エド君「…(いつの間にか書き換えられているテキストに目を背ける)」
どうしたエド君?
エド君「ま、まあこんな感じで2つのループを行うことができるわけだ…」
これなかなか便利そうだな。
エド君「ああ。これを使ってピラミッドを作ることもできる。」
ピラミッドってこんなかんじのやつ?

   エ
  エド君
 エド君すこ
エド君カワイイ

エド君「いや確かに形はそうだが変なピラミッド作ってんじゃねぇ。ピラミッドを作るにはfor文を3回使う。」
結構ややこしいんだな。

*
**
***

エド君「こんな感じの三角形ならそこまで複雑じゃねぇんだけどな。ピラミッドになると空白の処理も必要になる分ややこしくなる。」

#include <stdio.h>
int main(void) {
    int a;
    int i, j, k;
    printf("エド君「ピラミッドの段数を入力しろ。」");
    scanf("%d", &a);
    for (i = 1; i <= a; i++) {
        for (j = 1; j <= a - i; j++) {
            putchar(' ');
        }
        for (k = 1; k <= i * 2 - 1; k++) {
            putchar('*');
        }
        printf("\n");
    }

    return 0;
}


エド君「ピラミッドを作るときに各段の数が2個ずつ奇数で増えるからi*2-1で奇数の数列になるように設定している。」
なるほど。iが段数、jが空白部分、kが*の配置ってことか…

エド君「さて。今回はこの辺にしておくか。次はちょっと箸休めにプログラミングでミスしやすい点やプログラムの要素、今までにやったことの簡単なおさらいとするか。まあ肩の力抜くことも大切だからな。」