2001/11/26

前回の課題2について考えてみます。 (先週追加したヒントに沿って解説します)

課題2:日付を与えてその曜日を表示させるプログラムを作りなさい。

まずそのスクリプトがどんな機能を持つ部分から構成されるべきか 考えてみます。

  1. まず、指定した「年」「月」「日」の3つをそれぞれ 対応する変数に格納します。
  2. 1 で得られた状態をもとに、「曜日」を数値として計算します。
  3. 2 で得られた数値から「日月〜土」の文字を得ます。
  4. 最終的な出力を行います。

まず、cal.pl の内容を新しいファイル youbi.pl に貼り付けます。 カレンダー表示のための部分(その月の日数を求める部分、カレンダーの タイトルを出力する部分、カレンダーの曜日を出力する部分、カレンダーの 本体部分)はばっさり削除してしまってかまいません。 ただ削除したものを復活させる必要があるかもしれないし、削除しすぎる おそれがあるので、本当に消すのではなく行頭に「#」を付けてコメント にしてしまえばいいでしょう。


#!/usr/bin/perl

# コマンドライン引数をもらう
die "usage: cal month year\n" unless @ARGV == 2;
($mon, $year) = @ARGV;

# その月の末日を計算(2行目は閏年の計算)
#$lastday = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon - 1]
#         + ($mon == 2 && ($year % 4 == 0 && $year % 100 != 0 || $year % 400 == 0));

#print "      $year/$mon\n S  M Tu  W Th  F  S\n";

#foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
#    printf ('%2.2s ' , $_);
#    print "\n" unless ++$days % 7;
#}

#print "\n"; exit;

# 曜日を得る関数
sub getweek{
    local($year, $month) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + 1) % 7;
}

上のステップ1に対応させるためには引数を2から3に変更する必要が あります。日にちの値を格納する変数は $day とでもしてください。

(変更部分のみ表示)

die "usage: jperl youbi.pl year month day\n" unless @ARGV == 3;
($year, $mon, $day) = @ARGV;

ちゃんと値が格納できたかどうかそれらをprintを用いて表示させてみて下さい (これだけなら曜日を求める関数も不要ですが、特に削除する必要は ありません)。


die "usage: jperl youbi.pl year month day\n" unless @ARGV == 3;
($year, $mon, $day) = @ARGV;
print "$year年$mon月$day日\n";

I:\www>jperl youbi.pl 2001 11 12
2001年11月12日

上ができたら次はステップ2です。考えられる方法は2つあります。 まずはもともとの getweek をそのまま使って、その月の1日の 曜日を計算し、その値と $day から該当する日にちの曜日を求める方法。 もうひとつは getweek を書き直して3変数の関数にすること。 どちらも同じようなものですので、ここではそのまま使うことにします。

(コメント部は省略)

die "usage: jperl youbi.pl year month day\n" unless @ARGV == 3;
($year, $mon, $day) = @ARGV;
$i = ( &getweek($year, $mon) + $day - 1 ) % 7; 
print "$year年$mon月$day日は$i曜日です\n";

sub getweek{
    local($year, $month) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + 1) % 7;
}
I:\www>jperl youbi.pl 2001 11 12
2001年11月12日は1曜日です

ステップ3では数値から曜日を表す漢字を得ます。 これには ('日', '月', '火', '水', '木', '金', '土')という リストを用意し、その要素を番号の指定により得ればよいでしょう。

(コメント部は省略)

die "usage: jperl youbi.pl year month day\n" unless @ARGV == 3;
($year, $mon, $day) = @ARGV;
$i = ( &getweek($year, $mon) + $day - 1 ) % 7; 
$youbi = ('日', '月', '火', '水', '木', '金', '土')[$i];

print "$year年$mon月$day日は$youbi曜日です\n";

sub getweek{
    local($year, $month) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + 1) % 7;
}
I:\www>jperl youbi.pl 2001 11 12
2001年11月12日は月曜日です

別解も載せておきます:

#!/usr/bin/perl

# コマンドライン引数をもらう
die "usage: jperl youbi.pl year month day\n" unless @ARGV == 3;
($year, $month, $day) = @ARGV;

@youbi = ('日', '月', '火', '水', '木', '金', '土');

print "$year年$month月$day日は$youbi[&getdayofweek($year,$month,$day)]曜日です\n"; 


# 曜日を得る関数
sub getdayofweek{
    local($year, $month, $day) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + $day) % 7;
}

よくあるミスをあげておきます。うまくいかないときはこういうところに注意しましょう。


今日のレッスン

さて、いよいよ今度は HTML ファイルを作るスクリプトを書いてみます。

次のようなHTMLファイルを出力することを目標とします (行頭の行番号は説明の都合上つけたものです): sample01.html:

 1 <HTML>
 2 <HEAD>
 3 <TITLE>2000年3月の予定</TITLE>
 4 </HEAD>
 5 <BODY BGCOLOR="#FFFFFF">
 6 [<A HREF="perl02.html">「出力の工夫」に戻る</A>]
 7 <CENTER>
 8 <H1>2000 年 3 月</H1>
 9 <TABLE BORDER BGCOLOR="#C0C0FF">
10 <TR BGCOLOR="#80FF80">
11 <TH WIDTH="100">日</TH>
12 <TH WIDTH="100">月</TH>
13 <TH WIDTH="100">火</TH>
14 <TH WIDTH="100">水</TH>
15 <TH WIDTH="100">木</TH>
16 <TH WIDTH="100">金</TH>
17 <TH WIDTH="100">土</TH>
18 </TR>
19 <TR>
20 <TD VALIGN="top" WIDTH="100" >  <BR></TD>
21 <TD VALIGN="top" WIDTH="100" >  <BR></TD>
22 <TD VALIGN="top" WIDTH="100" >  <BR></TD>
23 <TD VALIGN="top" WIDTH="100" >1 <BR></TD>
24 <TD VALIGN="top" WIDTH="100" >2 <BR></TD>
25 <TD VALIGN="top" WIDTH="100" >3 <BR></TD>
26 <TD VALIGN="top" WIDTH="100" >4 <BR></TD>
27 </TR>
28 <TR>
29 <TD VALIGN="top" WIDTH="100" >5 <BR></TD>
30 <TD VALIGN="top" WIDTH="100" >6 <BR></TD>
31 <TD VALIGN="top" WIDTH="100" >7 <BR></TD>
32 <TD VALIGN="top" WIDTH="100" >8 <BR></TD>
33 <TD VALIGN="top" WIDTH="100" >9 <BR></TD>
34 <TD VALIGN="top" WIDTH="100" >10 <BR></TD>
35 <TD VALIGN="top" WIDTH="100" >11 <BR></TD>
36 </TR>
37 <TR>
38 <TD VALIGN="top" WIDTH="100" >12 <BR></TD>
39 <TD VALIGN="top" WIDTH="100" >13 <BR></TD>
40 <TD VALIGN="top" WIDTH="100" >14 <BR></TD>
41 <TD VALIGN="top" WIDTH="100" >15 <BR></TD>
42 <TD VALIGN="top" WIDTH="100" >16 <BR></TD>
43 <TD VALIGN="top" WIDTH="100" >17 <BR></TD>
44 <TD VALIGN="top" WIDTH="100" >18 <BR></TD>
45 </TR>
46 <TR>
47 <TD VALIGN="top" WIDTH="100" >19 <BR></TD>
48 <TD VALIGN="top" WIDTH="100" >20 <BR></TD>
49 <TD VALIGN="top" WIDTH="100" >21 <BR></TD>
50 <TD VALIGN="top" WIDTH="100" >22 <BR></TD>
51 <TD VALIGN="top" WIDTH="100" >23 <BR></TD>
52 <TD VALIGN="top" WIDTH="100" >24 <BR></TD>
53 <TD VALIGN="top" WIDTH="100" >25 <BR></TD>
54 </TR>
55 <TR>
56 <TD VALIGN="top" WIDTH="100" >26 <BR></TD>
57 <TD VALIGN="top" WIDTH="100" >27 <BR></TD>
58 <TD VALIGN="top" WIDTH="100" >28 <BR></TD>
59 <TD VALIGN="top" WIDTH="100" >29 <BR></TD>
60 <TD VALIGN="top" WIDTH="100" >30 <BR></TD>
61 <TD VALIGN="top" WIDTH="100" >31 <BR></TD>
62 <TD VALIGN="top" WIDTH="100" >  <BR></TD>
63 </TR>
64 </TABLE>
65 </CENTER>
66 </BODY>
67 </HTML>

すでにみた cal.pl と同様に月や年が $mon や $year 変数に格納されている状態とします。 cal.pl の11行目の部分(年・月の行と曜日の行の出力)に対応する部分をまず作ります。 上の sample01.html の1行目から18行目までを出力しなければなりません。

次のように記述すればとりあえず出力できます。 注意するのは"で囲まれた文字列の中に " という文字自身をいれるときは、 文字列の終端の"と区別するために前に\をつけて \" としなければならないことです。

print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>$year年$mon月の予定</TITLE>\n";
print "</HEAD>\n";
print "<BODY BGCOLOR=\"#FFFFFF\">\n";
print "[<A HREF=\"perl02.html\">「出力の工夫」に戻る</A>]\n";
print "<CENTER>\n";
print "<H1>$year 年 $mon 月</H1>\n";
print "<TABLE BORDER BGCOLOR=\"#C0C0FF\">\n";
.......
(略)
.......
print "<TH WIDTH=\"100\">土</TH>\n";
print "</TR>\n";

でもこれでは print や \n をずいぶん何度も書かなければなりません。 実は""で囲む文字列は複数行に渡ることができ、 しかもそのとき改行文字は改行文字として扱われるので、 次のように書きなおすことができます。

print "<HTML>
<HEAD>
<TITLE>$year年$mon月の予定</TITLE>
</HEAD>
<BODY BGCOLOR=\"#FFFFFF\">
[<A HREF=\"perl02.html\">「出力の工夫」に戻る</A>]
.......
(略)
.......
<TH WIDTH=\"100\">土</TH>
</TR>
";

しかしこれでも " の部分を \" としなければならず、不便です。 実はさらに次のようにかくこともできます。 赤い HERE は識別子で、適当に決めます。 通常の文字列の場合と同様に、最初の識別子を囲む引用符号の種類で、 中の変数の変数置換を行なうかどうかが変わります。 引用符号を省略すると " で囲まれたものと見なされます。

print <<"HERE";
<HTML>
<HEAD>
<TITLE>$year年$mon月の予定</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
[<A HREF="perl02.html">「出力の工夫」に戻る</A>]
.......
(略)
.......
<TH WIDTH="100">土</TH>
</TR>
HERE

それでは、cal.pl を hcal.pl という名前でコピーして、 11行目から18行目までの行頭に#を記述してコメントにして下さい。 そして上のいずれかをコメントした部分のかわりに挿入します (下の青い色の部分)。 これだけですと表が未完成ですので、日付のはいる部分は後回しにして、 ともかく sample01.html の64行目以降の部分も出力するように、 上の部分をまねて下の赤い色の部分を適切に記述して下さい。


#!/usr/bin/perl

# コマンドライン引数をもらう
die "usage: cal month year\n" unless @ARGV == 2;
($mon, $year) = @ARGV;

# その月の末日を計算(2行目は閏年の計算)
$lastday = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon - 1]
         + ($mon == 2 && ($year % 4 == 0 && $year % 100 != 0 || $year % 400 == 0));

#print "      $year/$mon\n S  M Tu  W Th  F  S\n";

print <<"HERE";
<HTML>
<HEAD>
<TITLE>$year年$mon月の予定</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
[<A HREF="perl02.html">「出力の工夫」に戻る</A>]
<CENTER>
<H1>$year 年 $mon 月</H1>
<TABLE BORDER BGCOLOR="#C0C0FF">
<TR BGCOLOR="#80FF80">
<TH WIDTH="100">日</TH>
<TH WIDTH="100">月</TH>
<TH WIDTH="100">火</TH>
<TH WIDTH="100">水</TH>
<TH WIDTH="100">木</TH>
<TH WIDTH="100">金</TH>
<TH WIDTH="100">土</TH>
</TR>
HERE

#foreach(("  ") x &getweek($year, $mon), 1 .. $lastday){
#    printf ('%2.2s ' , $_);
#    print "\n" unless ++$days % 7;
#}

#print "\n"; exit;

print "</TABLE>                      ┓
                                     ┃
                                     ┃     <== ここを適切に記述する
                                     ┃
";                                   ┛

# 曜日を得る関数
sub getweek{
    local($year, $month) = @_;
    if($month == 1 || $month == 2) {$year--;$month += 12;}
    int($year + int($year/4) - int($year/100) + int($year/400) + int((13*$month+8)/5) + 1) % 7;
}

hcal.pl をセーブして、月と年を指定して実行してみて下さい。

       jperl hcal.pl 11 2001
うまくいけば画面に表示されるはずです。 エラーがおきた場合はそのエラーメッセージをよく読んで修正を加えて下さい。 画面に表示するだけではブラウザで見ることができませんので、
       jperl hcal.pl 11 2001 > tmp.html
と、tmp.html というファイルにリダイレクトして下さい。 tmp.html というファイルができますので、これをブラウザで見てみましょう。 曜日だけのカレンダーができたでしょうか。



今回の課題

上のhcal.plの赤い色の部分を適当に補ったものを、 自分のHPに載せなさい。実際に出力された1行だけのカレンダー(?)も載せなさい。 どちらもリンクでかまいません。 完成できなくても、できたものを示しなさい。 なお、「〜に戻る」という部分は自分の環境にあわせて、適当に変更してください。


次回は本体部分を出力できるように、現在コメントになっている foreach ループ部を復活させ、書き換えます。お楽しみに。