« デビュー戦とか | メイン | コードブロック引数をオプションにできたら嬉しいんだけど »

2009年4月 9日

真か偽かは状況次第ってのは真理ではあるんだけど

Perlの真理値関連で若干はまったので書き留めておく。

my $hoge, $fuga;
$hoge =  $fuga  = 0; # $hoge: 0, $fuga: 0
$hoge = ($fuga) = 0; # $hoge: 1, $fuga: 0

下の代入では、リストがスカラーコンテキストで評価されているので、要素数の1が入る。少し考えるとなんでこうなるかはわかるんだけど、Perl的な考え方が自分に浸透してないと戸惑うね。

この関連でサブルーチンから値を返すときに、return undef;とかやるとハマるってのがある。

sub hoge{
    return undef;
}
my $hoge = hoge();
my @hoge = hoge();
 
print 'scalar' if $hoge;
print 'array'  if @hoge; #こっちは表示される!

@hogeは要素数が1の配列なので、ifに渡されると評価が真になってしまう。

こういった挙動を解決するための、Perlベストプラクティスでは、「裸のreturnを使う」ってのが推奨されている。でも、return;だとなんだかサブルーチンが正常終了したように見えてしまう。なので、return ();と書いたほうが、空の値を返していることが明確なのでこっちが個人的ベストプラクティス。return $hoge || ();みたいな書き方も出来るしね。

sub hoge{
    return ();
}
my $hoge = hoge();
my @hoge = hoge();
 
print 'scalar' if $hoge;
print 'array'  if @hoge; #どちらも出力されない

ただ、この場合にもちょっとしたはまりパターンがある。mapを使って配列の要素を一括変換かけるときなんかは、判定で偽になっても、場合によってはundefを結果セットに突っ込んで欲しいことも有り得るが、それがうまくいかない。

sub removeNaN{ #数字以外を削除する
    my $num = shift;
    return () unless $num =~ /\d/;
    $num =~ s/\D//g;
    return $num;
}
my @array = qw/10g 33 66m p151 hoge 9/;
my @nums = map {removeNaN($_)} @array;
print join ' ', @nums;#10 33 66 151 9 #要素数が減ってしまう!

こういうときは、明示的にreturn undef;と書かないといけないんだろうな。

最近はPerl5に真理値型がないってのはちょっと不便な気もしてきた。「世の中に絶対的な真とか偽とかはない、それはコンテキストで判断されるのだ」っていうPerlの考え方は好きなんだけど。

投稿者 Songmu : 2009年4月 9日 22:20