読者です 読者をやめる 読者になる 読者になる

bonar note

京都のエンジニア bonar の技術的なことや技術的でない日常のブログです。

[perl] return undef ではなく return がベストプラクティス。そしてハッシュではまる。

ダミアン先生によれば、 return undef じゃなくて return のみがベストプラクティスって話。例えば引数で受けた数が偶数どうかだけを判別するスクリプトがあったとします。

#!/usr/bin/perl

use strict;

sub is_even_number {
    my ($digit) = @_;

    if (($digit % 2) == 0) {
        return 1;
    }
    else {
        return undef;
    }
}

print is_even_number(shift) ? "yes?n" : "no?n";

is_even_number 関数は偶数なら1、それ意外ならundefを返します。これでほとんどの場合問題ないのですが、ふとした局面で配列で受けちゃったりするとやっかいな事になったりならなかったり。

my @test = is_even_number(3);
if (@test) {
    print "even!?n";
}

上記だと「even」と表示されてしまいます。これは@testがundefという要素1つだけもつ配列で、スカラーコンテキストで評価されて要素数の1となるからです。これは is_even_number の return undef を return だけに変える(何も返さない)という方法で回避できます。スカラーと配列のどっちで受けても大丈夫になります。


おまけにこんな書き方も出来ちゃったりしてスマート

my (@evens);
foreach my $num (4,5,6,7) {
    push @evens, is_even_number($num);
}
print scalar @evens . "?n"; # 2

なのですが、これ、はまりパターンがあって、ハッシュと併用した場合にちょっと危険です。あ、僕が間抜けなだけなのですが。

#!/usr/bin/perl

use strict;

sub is_even_number {
    my ($digit) = @_;

    if (($digit % 2) == 0) {
        return 1;
    }
    else {
        return;
    }
}

my %table = (
    1 => is_even_number(1),
    2 => is_even_number(2),
    3 => is_even_number(3),
    4 => is_even_number(4),
    5 => is_even_number(5),
    6 => is_even_number(6),
    7 => is_even_number(7),
    8 => is_even_number(8),
    9 => is_even_number(9),
);
print map { "[$_]=[$table{$_}]?n" } (1..9);

これを実行すると、

[1]=[7]
[2]=[]
[3]=[]
[4]=[1]
[5]=[6]
[6]=[]
[7]=[]
[8]=[1]
[9]=[]

となってしまいます。ハッシュは実際には配列なので、is_even_number が何も返さないことで順番が乱れて崩れてしまうんですね。崩れっぷりが素敵なのでふいにこれが現れると一瞬パニックになります。なりました、実際。


まあこれくらいのコードなら以下のように書けばOKなのでまったく問題ないのですが、

my (%table);
foreach my $num (1..9) {
    $table{$num} = is_even_number($num);
}
print map { "[$_]=[$table{$_}]?n" } (1..9);
- - - - - - -
[1]=[]
[2]=[1]
[3]=[]
[4]=[1]
[5]=[]
[6]=[1]
[7]=[]
[8]=[1]
[9]=[]

ただ、いったんハッシュを作って、そのリファレンスを配列にpushみたいなことをやりたい局面って結構あって、かつハッシュのキーに例のように関数入れちゃいたくなるんですね。。そういう時には

my %table = (
    1 => is_even_number(1) || undef,
    );

みたいな感じで書く週間を付けねば。でもなんか、これは僕が3流だからだと思うのですが、スカラーを期待されている関数はNGな時はundefを返してくれた方が使い勝手がいい気がするなあ。配列の場合は問答無用で return; のみのほうがよさそうですが。


そんな訳で、コードの色づけ機能素敵過ぎ。