久々のPerlの話題です。

きっかけは、これはEncodeのバグではないかという指摘を受けたこと。

#!/usr/bin/env perl
use v5.12;
use warnings;
use utf8;
use Encode qw/find_encoding/;
use CGI qw/escapeHTML/;
my $enc = find_encoding('UTF-8');
my $q = CGI->new("E=MC2");

say escapeHTML($enc->decode($q->param('E'))); # パラメターが存在すると動くのに
say escapeHTML($enc->decode($q->param('e'))); # 存在しないと動かない

何も返さない != undefを返す

違うのです。Perlの世界において、何も返さないこととundefを返すことは異なるのです。

以下をご覧下さい。

#!/usr/bin/env perl
use v5.12;
use warnings;
use Carp;
use CGI;
use Data::Dumper;

sub args {
  carp @_;
  return 0 + @_;
}

my $q = CGI->new("E=MC2");
say 'args($q->param("e")) == ', args($q->param("e"));
say 'args($q->param("E")) == ', args($q->param("E"));
say Dumper($q);

つまり、 args($q->param("e"))arg(undef) ではなく arg()と等価だということです。

CGI.pmの場合は、たとえばこうしてこの問題を回避できます。

#!/usr/bin/env perl
use v5.12;
use warnings;
use Carp;
use CGI;
use Data::Dumper;

sub args {
  carp @_;
  return 0 + @_;
}

my $param = CGI->new("E=MC2")->Vars;
say 'args($param->{"e"}) == ', args($param->{'e'});
say 'args($param->{"E"}) == ', args($param->{'E'});
say Dumper($param);

ではなんでCGI.pmはそういう仕様になっているのでしょうか?

そうでないと、こういう時に困ってしまうのですね。

#!/usr/bin/env perl
use v5.12;
use warnings;
use Carp;
use CGI;

my $q = CGI->new("name=dan&name=kogai");
my @name = $q->param('name');
say join ", " => @name;
my $name = $q->param('name');
say $name;

つまり、一つのキーに対して複数の値がありえる場合などに、複数の値を取り出せなくなっちゃうというわけです。

とはいえ、func(tion())func() の引数が tion() 次第で変わってしまうというのは確かにDWIMとは言いがたい。汎用性と速度ではなく可読性を重視したい場合には、可変長にならないように配列ではなく配列やハッシュのレファレンスを返すようにした方がよいでしょう。

Dan the Context-Driven Perl Monger