第41章 カレンダーの基礎  


カレンダーについても第26章のところで少しだけ 出てきました。任意の日付を入れると何曜日かわかる プログラムを考えてみましょう。実は、筆者は 大昔メモリ付き電卓でカレンダーが作れないかと 考えたことがあります。たとえば16日が金曜日なら 同じ月の23日も金曜日です。この時、日にちを7で割った 時の余りは同じです。16割る7は2あまり2です。 23割る7は3あまり2とどちらもあまりは2ですね。 (当たり前!)それでは、1月から12月まで 月初めの曜日を調べます。そして、日付を7で割ったとき の余りと月初めの曜日を調節する定数を与えます。 たとえば1日が月曜日ならその月の定数は0、 火曜日なら1、水曜日なら2、木曜日なら3、 金曜日なら4、土曜日なら5、日曜日なら6と 決めます。3月の定数が5ならば日にちにこれを足します。 14日なら19となります。これを7で割った余りは5です。 この余りが曜日を表します。0なら日曜日、1なら月曜日 2なら火曜日・・・という具合です。 では、この考えを元に1997年のカレンダー?を作ってみましょう。

#include <stdio.h> int main(void) { int date, mon, day; static mon_con[] = {2,5,5,1,3,6,1,4,0,2,5,0}; printf("月-->"); scanf("%d", &mon); printf("日-->"); scanf("%d", &date); day = (date + mon_con[mon - 1]) % 7; switch (day) { case 0: printf("日曜日です。\n"); break; case 1: printf("月曜日です\n"); break; case 2: printf("火曜日です\n"); break; case 3: printf("水曜日です\n"); break; case 4: printf("木曜日です\n"); break; case 5: printf("金曜日です\n"); break; case 6: printf("土曜日です\n"); break; default: printf("エラーです\n"); break; } return 0; }

これは簡単ですね。でもいくつか問題点があります。 これは、ある年しか使えません。月や日付の入力で おかしな数値を入力しても注意されません。 でもまあ、せっかく作ったのだから実行してみましょう。 月や日付の入力はユーザーの良識に任せることにします。

うーん。1997年の5月31日は 土曜日なのか・・・(筆者の誕生日です)

実は、カレンダー(曜日)については大昔から ある公式があります。(Zellerの公式)

西暦y年m月d日の曜日は

(y+[y/4]-[y/100]+[y/400]+[2.6m+1.6]+d)mod7

で与えられます。なお1月と2月については、 前年の13月、14月としなくてはいけません。 うっかりこの決まりを忘れていると

「アレーーー??変だなーー!!」

ということになります。 実は、筆者もこれを忘れて下のプログラムを書いたため、 大変な目にあいました。この値が0なら日曜日、1なら月曜日、 2なら火曜日・・・です。

[ ]記号は、Gauss記号といいます。 [x]とすると、これはxを越えない最大の整数を表します。
[4.2]=4
[0.95]=0
[-2.7]=-3
などとなります。

実は最近の数学ではGauss記号というのは 古いんだそうです。(筆者は詳しくは知らない)xを越えない 最大の整数は、「xの床(floor)」と言うそうです。 そして、Cでは、床を求める関数も用意されています。 その名もfloor関数です!

#include <math.h> double floor(double x);

これは、わざわざ関数を使わなくてもマクロで 実現できると思いませんか? 暇な人は、マクロを考えてみてください。

modは剰余演算子(moddulus operator)と言われるもので Cでは%ですね。

では、上の公式を利用してもう少し進んだカレンダーを 作ってみましょう。

今回は、main関数にあまりごちゃごちゃ書かない 方針で行ってみましょう。まず、入力関数 を作ります。入力された、日付の曜日を計算する 関数も作りましょう。最後にこの曜日を表示する 関数を作りましょう。それぞれの関数プロトタイプは 次のようにします。

void input_data(int *, int *, int *);//データ入力 int get_day(int, int, int); //曜日取得 void show_day_of_week(int); //曜日表示

そうするとmain関数は次のようになるでしょう。

int main(void) { int year, mon, date, day; //年、月、日、曜 input_data(&year, &mon, &date); if (mon <= 2) { mon += 12; year -= 1; } day = get_day(year, mon, date); show_day_of_week(day); return 0; }

あとは、各関数を書いていけばいいですね。 それと、「床」のマクロも作ってみましょう。

#define FL(x) ((int)(x) > (x) ? (int)(x) - 1 : (int)(x))

ここで、ちょっと3項演算子「?」の説明をします。

式1?式2:式3

の形となります。まず式1が評価され真であれば式2が、偽であれば 式3が評価されそれが条件式の値となります。

次に各関数を見てみましょう。

void input_data(int *y, int *m, int *d) { printf("西暦="); scanf("%d", y); printf("月 ="); scanf("%d", m); printf("日 ="); scanf("%d", d); return; }

yやmやdは、int型へのポインタであることに注意してください。 ここで、入力された年、月、日はmain関数でも有効です。

int get_day(int y, int m, int d) { int yobi; yobi = (y + FL(y / 4) - FL(y / 100) + FL(y / 400) + FL(2.6 * m + 1.6) + d) % 7; return yobi; }

これは、公式通りに書いただけです。マクロも使っていますね。

return 公式;

でもいいのですが長くなるので yobiという変数を作って上のように書きました。 (特別な意味はありません。)

void show_day_of_week(int d) { static char *yobi[] = {"日", "月", "火", "水", "木", "金", "土"}; printf("%s曜日です\n", yobi[d]); return; }

文字列の配列の扱いに注意してください。 漢字は1字でも2バイトなので文字列としなくてはいけません。 また、配列の初期化の方法も復習してください。 これらを、つなぎ合わせるとプログラムが出来上がりです。 もちろん#include <stdio.h>も忘れずに。

このプログラムで自分の生まれた曜日を調べてみましょう。


[Index][総合Index] [Previous Chapter] [Next Chapter]

Update Dec/09/1996 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。