LoginSignup
2
0

More than 5 years have passed since last update.

[Perl]連結したリストはスカラコンテキストで直感に反する返り値をとる

Last updated at Posted at 2017-09-27

こらもう絶対バグだと思った挙動が仕様だったので共有します。

問題のコード

use Moose::Role;
has zero => ( is => 'ro', isa => 'Num', default => 0 );
has num => ( is => 'ro', isa => 'ArrayRef[Num]', default =>sub {[1..9]} );
requires 'list';

という前提で、このロールを継承したモジュールの中で、

sub list {
    my $self = shift;
    ( $self->zero(), @{ $self->num() } );
}

sub list {
    my $self = shift;
    my @list = ( $self->zero(), @{ $self->num() } );
}

というように定義した場合、スカラコンテキストでの挙動に違いがあるという話です。
実は、リストコンテキストでは同じなのですが、前者をスカラコンテキストで評価すると@{ $self->num() }の値のみ返します

悲しいけどこれ仕様なのよね

perldocによると,

Comma Operator

Binary "," is the comma operator. In scalar context it evaluates its left
argument, throws that value away, then evaluates its right argument and\
returns that value.
This is just like C's comma operator. In list context, it's just the list
argument separator, and inserts both its arguments into the list.
These arguments are also evaluated from left to right.

つまり( $self->zero(), @{ $self->num() } )もしくはreturn ( $self->zero(), @{ $self->num() } )などと明示的にリストをreturnするように記載をしても、カンマで区切られた返り値は区切りに解釈され、スカラコンテキストでは

$self->zero();
@{ $self->num() };

の評価(要するに後者の値のみ)を返します。(リストコンテキストでは両方を連結したリストを返すので問題ありません)

調べてみてわかったのですが、これにはMooseは関係ありません。簡潔に記すと、

my $zero = 0;
my $num = [1..9];

sub list {
    return( $zero, @$num );
}

my ($x) = list();
my $y = scalar list();

という定義の下で、perlの仕様で、$xには0(リストの最初の値)が、$yには9(@$numの要素数)が代入されるということです。10にならないんです。直感に反しますよね?

ご指摘を受けたのでさらに掘り下げると、サブルーチンすら関係ありません。

my $zero = 0;
my $num = [1..9];

my ($x) = ( $zero, @$num );
my $y = scalar( $zero, @$num );

としても同様です。なんじゃこりゃ1

妥当な解決策

  • 一度配列に代入する

    my @return = $self->zero(), @{ $self->num() };
    
  • 一度無名配列を作りデリファレンスを返す

    @{ [ $self->zero(), @{ $self->num() } ] };
    
  • map関数を使う

    map {$_} $self->zero(), @{ $self->num() };
    

サブルーチン内では、returnはつけてもつけなくても一緒です。
sub{}の中の最後の評価値を自動的にreturnするのがPerlの仕様です。

個人的な意見

  • 明示的に()付きで書き添えた場合には()内を一つのリストと見傚して欲しいです。

  • またはscalar( $zero, @$num )のように、リストをCORE::scalar()に与えたときに警告を発しても良いんじゃないかと。2

SEE ALSO


  1. C言語からの影響とのこと 

  2. リンク先には"Because it's useful."とありますけどね。 

2
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0