カレンダ−・プログラムの改良(2)−サブルーチンとのパラメータのやりとり

さきに作ったカレンダープログラムでは、サブルーチンとパラメータのやりとりをしないで、サブルーチンでグローバル変数を書き換えるようにしました。しかし、メインルーチンとパラメータのやりとりをきちんとしておけば、サブルーチンをモジュール化できて、他のプログラムでも使うことができるので、正式な方法でサブルーチンとパラメータをやりとりするようにしてみました。

パラメータは、&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";
}

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