リストの要素数を取得する

配列ではなくてリストの要素数を取得するのにはどうすればよいか?というお話。

まず配列の場合はスカラコンテキストで評価するだけなんでとても楽。

 my @data  = qw/ a b c d /;
 my $count = @data; # 4

ではリストの場合はどうなるか?

配列と同じように書くなら

 my $count = qw/ a b c d /;

こうなるが、これは大間違いである。

リストをスカラコンテキストで評価すると最後の要素が返ってくるのでこの場合$countには「d」が入る。

とするならば一度配列で受け取り、その後にスカラコンテキストで評価するのが一番スマートなのかもしれない。

 my $count = my @tmp = qw/ a b c d /; # 4

しかしながら要素数を取得するだけなのに@tmpを広いスコープに置くのが嫌なのであればdoを使って囲ってしまうのが吉であろうか。

 my $count = do { my @tmp = qw/ a b c d / }; # 4

とかなんとか言いながら@tmpすら用意したくない不精な人にはこっちがオススメかもしれない。

 my $count = @{ [ qw/ a b c d / ] }; # 4

一度無名配列で受け取り、すぐさまデリファレンスしてそれをスカラコンテキストで評価するというわけだ。

ただし「無名配列使うのは反則だよ」と、意味のわからない正義感で物をいう人物にはこちらの方がシックリくるのではないかと思われる。

 my $count = map { 0 } qw/ a b c d /; # 4

mapを通すことによって戻り値が配列に変換されるので、それをスカラコンテキストで評価してやればまんまと要素数が取れるというわけだ。

でもmap使うなんて卑怯だよ!とか言うのであればgrep版でも用意してやれば文句あるまい。

 my $count = grep { 1 } qw/ a b c d /; # 4

だからmapやgrepみたいな標準関数使うな!と無茶を言うのであればもう自分でやるしかないな。

 my $count = sub { @_ }->( qw/ a b c d / ); # 4

無名サブルーチンを経由し、リストを@_配列に突っ込んでソレをそのまま返したのち、スカラコンテキストで評価して要素数を取得するというわけだ。

しかしながら一時変数もリファレンスも標準関数も自作関数も何もかも許せない頑固一徹な人にイチャモン付けられたのならもはやコレしかない。

 my $count = ( () = qw/ a b c d / ); # 4

ぬは。空リストで受け取った要素の数を取得するという方法だ。

これなら一時変数も用意しないしリファレンスも使用しないし標準関数も自作関数ですら使わなくて且つタイプ数さえも短くて超素敵!と納得してもらえるだろう。



さてさて、リストの要素数を取得するだけでも色々方法があるわけですが、そもそもリストの要素数が必要なケースってあんのかよって話ですよね。

結論はズバリ・・・、あるにはある。ということになりますか。



どういうケースか?



例えば、

 sub foo {
     return qw/ a b c d /;
 }

 $count = foo();          # d
 $count = ( () = foo() ); # 4

 sub bar {
     my $c = 'c';
     my $d = 'd';
     return (@_,$c,$d);
 }

 $count = bar('a','b');          # d
 $count = ( () = bar('a','b') ); # 4

このようにリストを返すような関数の場合、スカラコンテキストで受け取っても要素数を取ることができないのです。



では次の例を。

 sub baz {
     my @hoge = qw/ a b c d /;
     return wantarray ? @hoge : \@hoge;
 }

 $count = baz();          # ARRAY(0x1ca406c)
 $count = ( () = baz() ); # 4

wantarrayを使ってスカラコンテキストの場合に全然違う値が返ってくるケースですね。

この手の処理は結構あるので注意が必要です。



では最後の例を。

 my $str1  = 'a-b-c-d';
 my $str2  = 'a-b-c-d';
 
 $count = $str1 =~ m/\w/g;          # 1
 $count = ( () = $str2 =~ m/\w/g ); # 4

m//正規表現はスカラコンテキストで評価すると、マッチしたかどうかの真偽値が返ってきます。

だからマッチした数を取得したければ上記のようにしないといけないというわけです。さっきのwantarrayの例と同じ状態ですね。



と、まぁこのようにあるにはある、ということで終わりにしたいと思います。