カレンダ−・プログラム(2)−日数計算のサブル−チン

カレンダ−プログラムの最初のサブルーチンは次の通りです。これは、いろんなところで使えますので、きっと重宝すると思います。

sub find_first_last(){
@month_days = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
if(($year % 4) == 0 and ($year % 100) != 0 or ($year % 400) == 0) {
    $month_days[2] = 29;
    }
local ($days) = $year + int(($year-1)/4) - int(($year-1)/100) + int(($year-1)/400);
my $i = 0;
while ($i < $month ){
    $days += $month_days[$i];
    $i++;
    }
$first_day = $days % 7;  
$last_date = $month_days[$month];
}

最初の行は、一年の各月の日数を入れた配列です。配列は @month_day というように最初に @ をつけて表します。@ マークは、Eメールアドレスでも使いますが、ある Eメールアドレスを次のように print コマンドで使うとエラーになります。

print "jhon@abcnews.com";
こういう場合は、次のように書きます。
print "jhon\@abcnews.com";
$ 記号をそのまま表示する時も、\$ を使います。

一年は12か月なのに、この配列には要素が13個あるのを不思議に思いませんでしたか。配列は 0 から始まります。ところが一年は1月から始まります。後で、配列の要素を参照する時に、これではずれが起こるので、0月は 0日 とみなして、配列の 0 番目に 0 という要素をいれたわけです。

しかし、ここでやっかいなことがひとつあります。それは、うるう年には、2月が 29日あることです。そこで、うるう年を計算して、入力した年がうるう年にあたる場合は、配列の 2番目の要素を 29 にするようにしたのが、次の式です。

if(($year % 4) == 0 and ($year % 100) != 0 or ($year % 400) == 0) {
    $month_days[2] = 29;
    }
$year % 4 は、4 で割ったときの「余り」を表します。$year % 4 == 0 というのは、その年が 4 で割り切れる時、つまり 4年に一度のうるう年を表します。ところがうるう年のルールは少々複雑で、「うるう年は4で割り切れる年とする。ただし100で割りきれても、400で割りきれない年は平年とする。(たとえば、1600年は閏年ですが、1700年と1800年は100で割り切れますが、400では割り切れないので平年になります。) これにしたがってうるう年を計算したのが上記の式です。こういうのを「論理式」といい、ある条件にあてはまっているか否かの答えを 1 または 0 の価で返します。Perl では "and" "or" をそのまま使うことができるので、とてもわかり良いです。なお、($year % 100) != 0 の != は not equol という意味です。

@month_days の配列の 2番目の要素を表す変数は $month_days[2] と書きます。これを $month_day[2] と間違えて書いたら、@month_days の要素を参照できませんので、注意が必要です。

さて、カレンダーをつくるには、その年の1月1日が何曜日かを計算する必要があります。一年は365日ありますので、日曜日ではじまったらなら、日曜日で終わり、次の年は月曜日から始まることになります。現代の暦グレゴリオ暦は1582年10月15日(金)から採用され、それ以前はユリウス暦が使われていたので、このプログラムでは1583年以前のカレンダーは正確でありませんが、ずっとグレコリオ暦が使われていたとすると、紀元1年1月1日は月曜日になりますので、これを基準にうるう年による「ずれ」を調整すれば、その年の1月1日が何曜日かは次の式で割り出すことができます。次がその式です。

local ($days) = $year + int(($year-1)/4) - int(($year-1)/100) + int(($year-1)/400);
int は整数を得るファンクションです。すべての年が 4, 40, 100 で割り切れるはずがなく、小数が出てしまい、小数をそのままにしておくと、計算の結果がずれてしまいますので、int を束って小数点以下を切捨てているわけです。C では特に指定しないかぎり、変数は整数として扱われますので、int をつけなくても大丈夫です。上記の計算式は、C でカレンダープログラムを書いた時のものを持ってきたので、最初 int を付け忘れていましたが、ある時、間違いに気づいて直しました。

Perl の変数は特に指定しないかぎり、すべてグローバル変数となり、メインルーチンであろうがサブル−チンであろうが、同じ名前の変数が出てくれば、すべて同じ価が入っているとみなします。$days をこのサブルーチン内だけで使いたいときは local ($days) とするか、my $days とするか、ふたとおりの方法があります。local や my は一度使えばそれで良く、繰りかえす必要はありません。

今度は一ヶ月分のカレンダーをつくるために、その月の1日の曜日を知る式です。1月1日が何曜日かが分かっているのですから、その前の月までの日数を合計して 7 で割ることによって、曜日を割り出すことができます。各月の日数は @month_days に入っていますから、つぎつぎ足していけばよいのです。それが次の式です。

my $i = 0;
while ($i < $month){
    $days += $month_days[$i];
    $i++;
    }
$i はカウンターで、$i++ は $i = $i + 1 という意味ですから、1 づつふえていきます。while ($i < $month){....} は、カウンターがその月になるまで、{} 内の仕事をするという意味です。while (....){....} は良く使う構文ですが、カウンターはかならず $i = 0 のように初期化しておかないと、ちゃんと働いてくれません。

$days += $month_days[$i]; は何をしているかというと、

$days = $days + $month_days[$i];
と同じことをしているわけで、$days に $month_days[$i] を加算しているわけです。ここまでできれば、その月の1日の曜日と、その月の最後の日が何日かが次のようにわかりますので、これをそれぞれの変数にいれて、このサブルーチンの仕事は終わります。
$first_day = $days % 7;  
$last_date = $month_days[$month];
$first_day は、0 だったら日曜日、1 だったら火曜日、... 6 だったら土曜日になります。

[前のぺ−ジ] [目次] [次のぺ−ジ]