配列やハッシュを HTML で出力する

先ほどのエントリでは HTML 文によりページを表示しましたが、ただ単に HTML 文を print しているだけだったので、これでは HTML ファイルを表示してるのと、なんら変わりません。
そこで、今度は変数を埋め込んでページを表示してみます。スカラーは p 要素として、配列は ol 要素として、ハッシュは dl 要素として表示してみたいと思います。
関数に切り分けたら、ちょっと長くなりましたが、こんな感じになりました。

#!/usr/local/bin/perl

use strict;

sub print_array(\@) {
    print "<ol>\n";
    print "<li>$_</li>\n" for (@{ $_[0] });
    print "</ol>\n";
}
sub print_hash(\%) {
    my $h = shift;
    print "<dl>\n";
    print "<dt>$_</dt><dd>$h->{$_}</dd>\n" for (keys %$h);
    print "</dl>\n";
}
sub print_var {
  while (my $v = shift) {
    my $type = ref($v);
    if    ($type eq '')       { print "<p>$v</p>\n"; }
    elsif ($type eq 'SCALAR') { print "<p>$$v</p>\n"; }
    elsif ($type eq 'ARRAY')  { print_array(@$v); }
    elsif ($type eq 'HASH')   { print_hash(%$v); }
    elsif ($type eq 'REF')    { print_var($$v); }
    else                      { print "<p>$type</p>\n"; }
  }
}

# メイン処理
{
    print "Content-Type: text/html\n\n";
    print <<HEADER;
<html>
<head><title>Printing Variables</title></head>
<body>
HEADER

    my $sc = "Hello, my name is Palmo!";
    my $ar = ["First", "Second", "Third"];
    my $ha = {Apple => "Red", Banana => "Yellow", Peach => "Pink"};

    print_var($sc, $ar, $ha);

    print <<FOOTER;
</body>
</html>
FOOTER
}

print_array 関数は、渡された配列を ol 要素として出力します。同じく print_hash 関数は渡されたハッシュを dl 要素として出力します。これらの関数はプロトタイプによってリファレンスを受け取るようになっています。リファレンスをやりとりする事で、無駄なコピーを抑える事ができます。
print_var 関数は、渡された値のリストをそれぞれ HTML で出力します。ref 関数スカラー変数の中身がリファレンスじゃないなら空文字を、リファレンスなら「何」を参照しているのかを表した文字列を返してくれるのでしたね。これを利用して、リファレンスが渡された時も、中身を適切に表示できるようにしました。if 文が羅列していて、ちょっと汚いですね……。(^_^;)
メイン処理でページを表示しています。$sc はスカラー値、$ar は無名配列へのリファレンス、$ha は無名ハッシュへのリファレンスがそれぞれ入りますね。これを print_var 関数に渡すと、ちゃんと表示してくれるはずです。
var2html.cgi 実行結果
上手く表示できました。(^_^)


ところで if 文の羅列が嫌な時は「Switch」モジュールを使えば良いそうです。このモジュールを use すると、C言語などでお馴染みの switch 構文が使えるようになります。
Switch モジュールを使って上のスクリプトの print_var 関数を書き換えると、以下のようになります。

use Switch;

sub print_var {
  while (my $v = shift) {
    switch (my $type = ref($v)) {
        case ''        { print "<p>$v</p>\n"; }
        case 'SCALAR'  { print "<p>$$v</p>\n"; }
        case 'ARRAY'   { print_array(@$v); }
        case 'HASH'    { print_hash(%$v); }
        case 'REF'     { print_var($$v); }
        else           { print "<p>$type</p>\n"; }
    }
  }
}

if 文を羅列するよりは、断然読みやすいですね。
Switch モジュールによる switch は、C言語の switch よりも優れていて case 文には数値に限らず色々な条件を書く事ができます。上のように文字列による case も可能ですし、リストやハッシュなどを書けばその中に switch 対象が存在するかどうか調べられ、正規表現を書けばマッチングが行なわれ、コードブロックを渡せば対象を引数として実行されてその返り値が調べられます。
何でもありですね。(^_^;)