今度はカレンダー・プログラムにメニューを追加します。その月のカレンダーを表示したら、次のようにメニューを出します。
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"; }