1日目 3日目

2日目

目標

課題


課題 2

カレンダプログラムを作ってみよう(1)

始めに、TeraTerm を用いて PorgE1 に login し、Windows 上から ProgE1 のディスクが 利用できるように準備をする。その後、ProgE1 上に kadai02 というフォルダを作成しておく。 さらに O ドライブの examples フォルダの kadai02 フォルダの中のファイルをコピーしておく。

cal の実行

TeraTerm 上で cal と実行すると次のような出力が得られる。

前回も述べたように下線がついている部分は、「プロンプト」と呼ばれるコマンドの 入力待状態を示す記号であり、入力する必要はない。
[user99@proge1]~% cal
      10月 2007
日 月 火 水 木 金 土
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

[user99@proge1]~% cal 11 2007
      11月 2007
日 月 火 水 木 金 土
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

[user99@proge1]~%

このようなカレンダを表示するプログラムを C で書いてみよう。 ただし、表示は日本語でなくても良いこととする。

日本語を表示したい場合は、プログラムのソースが「Unicode」で保存されている事を確認すること。

「べた」なカレンダプログラム (1)

各自の O ドライブの「examples」フォルダからコピーしてきた mycal1.c というファイルを 秀丸で開いてみる。

最初に思い付くのは、課題1で学習した printf をそのまま使う方法である。 たとえば、11月のカレンダを出力するのであればこんな感じ。

/* mycal1.c */
#include <stdio.h>

main()
{
    printf("             1  2  3\n");
    printf(" 4  5  6  7  8  9 10\n");
    printf("11 12 13 14 15 16 17\n");
    printf("18 19 20 21 22 23 24\n");
    printf("25 26 27 28 29 30\n");
}

では、このプログラムを実行できるように、コンパイル作業を行う。 コンパイルは前回行ったように cc コマンドを使うのであるが、 今回は、出力される実行形式のファイル名を指定することにする。

[user99@proge1]~% cd kadai02
[user99@proge1]~/kadai02% cc mycal1.c -o mycal1
[user99@proge1]~/kadai02%
口頭ではすでに説明しているが、プロンプトの [] の後ろに表示されているのは自分が今作業を行っているディレクトリ(フォルダ)名である。 "~" がログインした直後 == ウィンドウズから見た際の O ドライブを意味し、 "/" で階層の区切りを示しているのであった。 最初に kadai02 ディレクトリに移動するために、cd kadai02と入力している。

前回のコンパイルと異なるものは水色で示した 「-o mycal1」 の部分である。 このように指定することで、実行形式のファイル名を a.out から mycal1 に変更することができる。
    ・このように振舞いを変えるための指定をオプションと呼ぶ。
    ・今後は、このように出力ファイル名を指定することで同じディレクトリに複数の実行形式を作成する。

コンパイルが成功すれば、実際に実行してみる。実行するには ./mycal1と入力する

[user99@proge1]~/kadai02% ./mycal1
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30
[user99@proge1]~/kadai02%

「べた」なカレンダプログラム (2)

さて、mycal1 の出力を良く観察すると、

となっていることがわかる。 というわけで、出力を「1日」分ごとに分解すると、次のような mycal2.c を 作ることができる。

/* mycal2.c */
#include <stdio.h>

main()
{
    printf("   "); /* 日曜日 */
    printf("   "); /* 月曜日 */
    printf("   "); /* 火曜日 */
    printf("   "); /* 水曜日 */
    printf("  1"); /* 木曜日 */
    printf("  2"); /* 金曜日 */
    printf("  3"); /* 土曜日 */
    printf("\n");  /* 7日分出力したので改行 */
    printf("  4");
    printf("  5");
    printf("  6");
    printf("  7");
    printf("  8");
    printf("  9");
    printf(" 10");
    printf("\n");  /* 7日分出力したので改行 */
    printf(" 11");
    printf(" 12");
    printf(" 13");
    printf(" 14");
    printf(" 15");
    printf(" 16");
    printf(" 17");
    printf("\n");  /* 7日分出力したので改行 */
    printf(" 18");
    printf(" 19");
    printf(" 20");
    printf(" 21");
    printf(" 22");
    printf(" 23");
    printf(" 24");
    printf("\n");  /* 7日分出力したので改行 */
    printf(" 25");
    printf(" 26");
    printf(" 27");
    printf(" 28");
    printf(" 29");
    printf(" 30");
    printf("\n");  /* 月の終わりなので改行 */
}
"/*" と "*/" ではさまれた部分は「コメント(comment)」と呼ばれ、 コンパイルの際には無視されるため、プログラムの実行には影響を与えない。 適切にコメントをつけることで、プログラムが読みやすくなるので、上手に利用することが重要である。 コメントには日本語も積極的に用いて後から見たときに、その内容を理解しやすいようにしたほうが良い。

プログラムは、かなり長くなったが、内容(==やっていること)は、mycal1.c や先週の hello.c と何も変わらないし、新しいことも特にない。 実際、コンパイルして実行してみると mycal1 と(ほぼ)同じ出力が得られるはずである。

[user99@proge1]~/kadai02% cc mycal2.c -o mycal2
[user99@proge1]~/kadai02% ./mycal2
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30
[user99@proge1]~/kadai02%

プログラムっぽく変更してみる

では、mycal2.c をもとに、プログラムらしいプログラムに変更していく。 最終形はこのようになる予定である。

/* mycal3.c */
#include <stdio.h>

main()
{
        int i;

        /* 月始めの「空白」 */
        for (i = 0; i < 4; i = i + 1) {
                printf("   ");
	}

        /* 日付の表示 */
        for (i = 1; i <= 30; i = i + 1) {
                printf(" %2d", i);

                /* 「土曜日」ならば、改行する */
                if ((i % 7) == 3) {
                        printf("\n");
                }
        }

        /* 最後には改行 */
        printf("\n");
}

実際にコンパイルしてみて、同じ表示になることを確認してみよう。

[user99@proge1]~/kadai02% cc mycal3.c -o mycal3
[user99@proge1]~/kadai02% ./mycal3                                                                                    /tmp
              1  2  3
  4  5  6  7  8  9 10
 11 12 13 14 15 16 17
 18 19 20 21 22 23 24
 25 26 27 28 29 30
[user99@proge1]~/kadai02%

同じであることが確認できたら、どこがどう変わっていったのかを一つづつ見ていこう。


変数

最初に現れた新しい文は

int i;

である。 これは、プログラムらしいプログラムのために新たに必要になったものであり、mycal2.c には対応するものはない。 上の文は、「int 型という整数データを入れるための領域として i という名前の変数を用意する」事を意味している。

変数とは、プログラム中で何かを記録するために用いられるもので、数学で出てきた変数に良く似ている。 その値はプログラムの実行中に変更することができるという意味では数学の変数に似ているが、 プログラム中の変数は、各文を実行している特定の瞬間には、ある決まった値しか取ることができないという点で異なる。 そのため、良く何かをいれる「箱」に例えられる。

とりあえずは、「整数を記憶する i というラベルのついた箱が用意されたのだな」位の理解で良い。

変数について簡単にまとめた


月初めの空白>

まず、mycal2.c では、月始めの「4日分の空白」のために、

	printf("   ");
	printf("   ");
	printf("   ");
	printf("   ");

と同じ文を繰り返し書いていた。 このような「繰り返し」処理はコンピュータでもっとも良く行われる処理のひとつである。 mycal3.cでは、この部分を C で利用できる繰り返し処理のうちの1つの for文を用いて書き直してある。

for 文は次のような構成になっている。

  for (最初に1度だけ行う文; 続けるかどうかの判定文; 毎回最後に行う文) {
           <繰り返し処理>;
  }

繰り返し処理を始めた場合、いつ繰り返しを終えれば良いのかが明らかでないと終了しないプログラムになってしまう。 今の場合、「4回同じ動作を行う」というのが、終えるための条件である。 そのため、プログラムで、今何度目の繰り返し処理を行っているのかを記録しなければならない。 用意された変数 i はこの記憶のために用いられている。 最初の for文をもう一度見直してみると、

        /* 月始めの「空白」 */
        for (i = 0; i < 4; i = i + 1) {
                printf("   ");
	}
  1. 最初に i = 0 が実行される。 その結果、変数 i に 0 が代入される。
  2. 判定文を確認。変数 i と数値3の比較が行われる。 0 は 4 より小さいので、繰り返し処理を行うことにする。
  3. 繰り返し処理である printf("   ");が実行される。 画面に空白が3つ出力される。
  4. i = i + 1が実行される。 その結果、変数 i の中身は 1 となる。
  5. 判定文を確認。変数 i と数値3の比較が行われる。 1 は 4 より小さいので、引き続き繰り返す。
  6. 繰り返し処理である printf("   ");が実行される。 画面に空白が3つ出力される。
  7. i = i + 1が実行される。 その結果、変数 i の中身は 2 となる。
  8. 判定文を確認。変数 i と数値3の比較が行われる。 2 は 4 より小さいので、引き続き繰り返す。
  9. 繰り返し処理である printf("   ");が実行される。
  10. i = i + 1が実行される。 その結果、変数 i の中身は 3 となる。
  11. 判定文を確認。変数 i と数値3の比較が行われる。 3 は 4 より小さいので、引き続き繰り返す。
  12. 繰り返し処理である printf("   ");が実行される。
  13. i = i + 1が実行される。 その結果、変数 i の中身は 4 となる。
  14. 判定文を確認。変数 i と数値3の比較が行われる。 4 は 4 より小さくないので、繰り返しを終了する。

この結果、printf("   ");が4回実行されることになる。


forによる繰り返し処理(2)と条件文if

次に日付を出力する部分を見てみると、数字が変わっていく点を除けば、やはり for で表現できる事に気付く。

変数の中身を表示させるを 参考に考えると printf(" %2d", i);で表現できることが判る。(判る? :-)

        /* 日付の表示 */
        for (i = 1; i <= 30; i = i + 1) {
                printf(" %2d", i);
        }

しかし、これでは、1 日から 30日までが一行に(実際には画面の幅で改行されるように見えるが)表現されてしまい、 カレンダにならない。 カレンダにするには、ある特定の場合(今の場合は土曜日である 3, 10, 17, 24日)のあとだけ改行"\n"を出力する必要がある。

このような条件付きで特定の処理をするための方法の一つとして C 言語では if 文を用いる。

if文の基本型は次のような構成になっている。

 if (条件1) {
     <条件1が成立した場合の処理>
 } else if (条件2) {
     <条件1が成立せず、条件2が成立した場合の処理>
 } else {
     <どの条件も成立しなかった場合の処理>
 }

条件が一つで良い場合は else if は不要であるし、条件が成立しない場合が 不要であれば省略もできる。

従って、「3日」の後ろに改行を出す場合は

    if (i == 3) {
        printf("\n");
    }

と書くことができる。 ここで、"==" は両辺が等しいかどうかを検査する演算子である。

残りの土曜日についても処理を追加する。 else if 構文を使うのであれば、次のように記述できる。

    if (i == 3) {
        printf("\n");
    } else if (i == 10) {
        printf("\n");
    } else if (i == 17) {
        printf("\n");
    } else if (i == 24) {
        printf("\n");
    }

が、処理が同じなので条件をまとめることもできる。 論理和演算子を用いると次のようにまとめられる。

    if (i == 3 || i == 10 || i == 17 || i == 24) {
        printf("\n");
    }

あるいは、3, 10, 17, 24 が共通の性質として、「7で割ったあまりが3」というのを持つことに気付くと、 剰余演算子(%) を用いて次のように簡単に書くこともできる。

    if ((i % 7)  == 3) {
        printf("\n");
    }

あとは、プログラムの最後に、改行コードを出力すれば良いだけである。

というわけで、mycal3.c と mycal2.c は同等の事を行っていることが理解できたと思う。


1日目 表紙 3日目

tacha@tack.fukui-med.ac.jp
$Id: 02.html,v 1.1 2007/10/25 16:03:47 tacha Exp $