さきに作ったカレンダープログラムでは、サブルーチンとパラメータのやりとりをしないで、サブルーチンでグローバル変数を書き換えるようにしました。しかし、メインルーチンとパラメータのやりとりをきちんとしておけば、サブルーチンをモジュール化できて、他のプログラムでも使うことができるので、正式な方法でサブルーチンとパラメータをやりとりするようにしてみました。
パラメータは、&find_first_last($year, $month) というように()でくくってサブルーチンに渡します。サブルーチンのほうでは、これを @_ という特別な配列で受け取りますので、$year, $month は次のようにして受け取ります。
$year = $_[0]; $month = $_[1];この時、メインルーチンとサブルーチンのパラメータの個数や、順序を間違えないように気をつけましょう。 サブルーチンから、価を返すには、価が一個なら return $value; で済みますが、二個以上の場合は、次のように、配列に入れて返します。
$return_value[0] = $first_day; $return_value[1] = $last_date; return @return_value;これは次のように書くこともできます。
return ($first_day, $last_date);これを受け取るには、メインルーチンに用意した変数に代入するだけです。配列で受け取った時は、次のように個々の変数に価をいれておきます。
@find_value = &find_first_last($year, $month); $first_day = $find_value[0]; $last_date = $find_value[1];これも次のように書くことができます。
($first_day, $last_date) = &find_first_last($year, $month);
このような要領で書き換えたメインルーチンは次の通りです。
#!/usr/bin/perl
#
# calendar02.pl
#
if($ARGV[0] eq ""){
print "Input year and month.\n";
exit(0);
}
if($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);
}
($first_day, $last_day) = &find_first_last($year, $month);
&show_a_month($year, $month, $first_day, $last_date);
exit(0);
日数計算のサブルーチンでは、サブルーチンで使う変数をすべてローカルにするために locoal () を使って、一度に定義してしまいました。結果は次の通りです。
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;
}
$days = $year + int(($year-1)/4) - int(($year-1)/100) + int(($year-1)/400);
$i = 0;
while($i < $month){
$days += $month_days[$i];
$i++;
}
$first_day = $days % 7;
$last_date = $month_days[$month];
return ($first_day, $last_day);
}
カレンダーを表示するサブルーチンでは、月の表示を、数字でなく、名前で行うため
@month_name = ("", "January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
という配列を用意しました。月は1月から始まるので、配列の 0番目はブランクにしてあります。文字列配列は次のように書くこともでき、この場合は " (引用符) はいりませんし、区切りは , (カンマ) でなく、スペースになります。
@month_name = qw(dummy January Feburary March April May June July August September October November December);この書き方では、0月を一応 dummy としておきました。次は書き換えたサブルーチンです。
sub show_a_month(){
local (@month_name, $year, $month, $i, $date, $first_day, $last_date);
($year, $month, $first_day, $last_day) = @_;
@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";
$i = 0;
while($i < $first_day){
print " ";
$i++;
}
$date = 1;
while($date <= $last_date){
print " ";
if ($date < 10){
print " ";
}
print $date;
if(($first_day + $date -1) % 7 == 6){
print "\n";
}
$date++;
}
print "\n";
}