カレンダ−・プログラムの改良(4)−メニューの追加

今度はカレンダー・プログラムにメニューを追加します。その月のカレンダーを表示したら、次のようにメニューを出します。

while(1){
    ($first_day, $last_day) = &find_first_last($year, $month);
    &show_a_month($year, $month, $first_day, $last_date);
    print " N-ext B-ack Q-uit ";
    ($year, $month) = &select($year, $month);
    }
exit(0);
N-ext の前に空白があるのは、カレンダーの日付の数字と頭をそろえるため、Q-uit の後の空白は、カーソルプロンプトが t の文字とくっついてメニューが見えにくくなるのを防ぐためです。メニューの N, B, Q には次の意味を持たせました。

これを入力するとこうなる
N翌年の同じ月を表示する
n次月を表示する
b先月を表示する
B昨年の同じ月を表示する
リターン今月を表示する
q終了する

while(1){ .... } の構文は無限ループを作ります。これを作ったら、かならず、ループから抜けて終了する命令を書いておかなければなりません。それは &select というサブルーチンに次のように書いておきました。プログラムの作成に失敗して無限ループから抜けられない時は コントロール + C のキー操作でプログラムを終わらせることができますから、パニックになりませんように。

さて、&select は次のとおりです。

sub select(){
local ($input, $year, $month);
    
($year, $month) = @_;

$input = <STDIN>;
chomp $input;
print "\n";
if($input eq 'q'){
    exit(0);
    }
elsif($input eq 'b'){
    $year = $year - ($month == 1);
    $month = $month - ($month > 1) + 11 * ($month == 1);
    }
if($input eq 'B'){
    $year--;
    }
if($input eq 'n'){
    $year = $year + ($month == 12);
    $month = $month + ($month < 12) - 11 * ($month == 12);
    }
if($input eq 'N'){
    $year++;
    }
if($input eq ''){
    $year = $this_year;
    $month = $this_month;
    }
return ($year, $month);
}
このサブルーチンでのポイントは、キーボードからの入力を取りこむ $input = <STDIN> というラインと、年と月の計算に使っている論理式のふたつです。

<STDIN> は正確には標準入力から価を得ることで、得た価は $_ という変数にとりこまれますが、$input = <STDIN> と書いたほうが、分かりやすいので、そうしました。$input はリターン・キーを押した時点で入力されますので、やりなおしが効くので便利ですが、リターン・キーを押さなければならないのが面倒という欠点もあります。次のステップで、キーからの直接入力を受けつけるようにしますが、今回は、このままにしておきます。

chomp $input;
というのは、文字列の最後の改行記号などを取り除くファンクションで、Perl では、良く使われます。$input eq '' がリターン・キーを押したことを表すのは、$chomp で改行記号が取り去られて、'' (何もないことをあらわす) となったのです。

'n' を入力した場合、次の月に繰り上がっていくのですが、もし、現在の月が 12月なら、翌年の 1月にしなければなりません。それで、

$year = $year + ($month == 12);
という式を書きました。ここで、かっこにくくった ($month == 12) という式は、もし $month が 12 なら 1 、そうでなければ 0 になるという式です。こう書けば、12月の場合は 1年くりあがるという命令になります。
$month = $month + ($month < 12) - 11 * ($month == 12);
上の式では、月の繰り上がりを、12月以下のときは、1月増やすというのを $month + ($month < 12 ) で表しています。12月の時は 1月にしてやらなければなりませんので、12 kから 11 を引いて、むりやり 1月にしています。それが - 11 * ($month == 12) という式です。 * はかけ算を表わします。($month == 12) は 0 か 1 の価を返しますから、その月が 12月でなければ 11 * 0 = 0 で 11 がキャンセルされてしまいます。ちょっとわかりにくかったかもしれませんが、実際にやってみると、案外簡単です。論理式に限らず、コンピュータのプログラムは、どれも、読むよりも書くほうが簡単ですので、書きながらならっていくのが一番です。

カレンダープログラム Version 0.4 の全体は、次のようになります。わかりやすくするため、コメントをつけておきました。次のプログラムをカット・アンド・ペーストで、あなたのエディタに取りこんで、実行してみてください。
#!/usr/bin/perl
#
# calendar04.pl
#

# 現在時刻を取りこむ
($this_year, $this_month, $today) = &get_current_time;
# コマンドラインの解析
if($ARGV[0] eq ""){
    $year = $this_year;
    $month = $this_month;
    }
elsif($ARGV[1] eq ""){
    @parameters = split /\D/, $ARGV[0];
    if($parameters[1] eq ""){
        print "Input year and month.\n";
        exit(0);
        }
    else{
        $year = $parameters[0];
        $month = $parameters[1];
        }
    }
else{
    $year = $ARGV[0];
    $month = $ARGV[1];
    }
if($month < 1 or $month > 12){
    print "Month is out of range.\n";
    exit(0);
    }
# カレンダーの表示とメニュー
while(1){
    ($first_day, $last_day) = &find_first_last($year, $month);
    &show_a_month($year, $month, $first_day, $last_date);
    print " N-ext B-ack Q-uit ";
    ($year, $month) = &select($year, $month);
    }
exit(0);

sub select(){
local ($input, $year, $month);
($year, $month) = @_;
# キーボードからの入力
$input = <STDIN>;
chomp $input;
print "\n";
# 選択と年月の計算
if($input eq 'q'){
    exit(0);
    }
elsif($input eq 'b'){
    $year = $year - ($month == 1);
    $month = $month - ($month > 1) + 11 * ($month == 1);
    }
if($input eq 'B'){
    $year--;
    }
if($input eq 'n'){
    $year = $year + ($month == 12);
    $month = $month + ($month < 12) - 11 * ($month == 12);
    }
if($input eq 'N'){
    $year++;
    }
if($input eq ''){
    $year = $this_year;
    $month = $this_month;
    }
return ($year, $month);
}

sub get_current_time(){
local ($sec, $min, $hour, $date, $month, $year, $day);
# 現在時刻の取り込み
($sec, $min, $hour, $date, $month, $year, $day) = localtime(time);
# 年月の補正
$year = 2000 + $year - 100;
$month++;
return ($year, $month, $date);
}

sub find_first_last(){
local($year, $month, @month_days, $days, $i, $first_day, $last_day, @return_value);
($year, $month) = @_;
@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;
    }
# 1月1日の曜日の計算
$days = $year + int(($year-1)/4) - int(($year-1)/100) + int(($year-1)/400);
$i = 0;
while($i < $month){
    $days += $month_days[$i];
    $i++;
    }
# その月の1日の曜日と日数を返す
$first_day = $days % 7;  
$last_date = $month_days[$month];
return ($first_day, $last_date);
}

sub show_a_month(){
local (@month_name, $year, $month, $i, $date, $first_day, $last_date);
($year, $month, $first_day, $last_date) = @_;
@month_name = ("", "January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
# カレンダーの表示
print "    $month_name[$month], $year\n";
print " Su Mo Tu We Th Fr Sa\n";
# 1日までの空白の表示
$i = 0;
while($i < $first_day){
    print "   ";
    $i++;
    }
# 日付の表示
$date = 1;
while($date <= $last_date){
    print " ";
    if ($date < 10){
        print " ";
        }
    # 現在日のカラー表示(太字、緑色)
    if ($year == $this_year and $month == $this_month and $date == $today){
        print "\033[1;32m";
        }
    print $date;
    print "\033[0m";
    # 土曜日でおりかえす
    if(($first_day + $date -1) % 7 == 6){
        print "\n";
        }
    $date++;
    }
print "\n";
}

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