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

2つの論理和演算子の優先順位について(|| と or)

なんとなく、はてなにメモするのがいいかなと思ったので、今日確認したことを。コメントに書いた説明が正確かどうか不安もあるけど。

今回わかったことは

...
sub property {
  my $self = shift;
  $self->{'_arg'} = shift || return $self->{'_arg'};
}
...

で set/get みたいな効果になるけど、このまま || を or に変えるとうまくいかないということ(実際は shift する/した値の内容もチェックしたほうがいいかも)。「制御構造には || でなく or を使う」と単純に理解していたので少し理解に苦しんだものの、代入演算子 = に対する両者の優先度の違いを確認してなんとか飲み込めた。

test_perlop.pl*1

{
    package test_perlop; use strict; use warnings;

    # or は = より優先度が低く、
    # || は = より優先度が高いことを確認する。
    my $undef = undef; my $str = 'str';
    my $v1; $v1 = $undef || $str;
    my $v2; $v2 = $undef or $v2 = $str;
    printf " \$v1 = %s\n", $v1;
    printf " \$v2 = %s\n", $v2;

    # or の場合:
    # $arg=shift が評価された後に return $arg が評価されるので
    # $arg には shift された undef が代入され、それが返される。
    sub hoge { my $arg = 'hoge'; $arg = shift or return $arg; }

    # || の場合:
    # $arg = shift が評価される前に shift || return ... が評価される、
    # すなわち shift が偽となると即 return $arg が評価され
    # shift からの代入は行われないので $arg の内容は 'foo' のまま返される。
    sub foo { my $arg = 'foo'; $arg = shift || return $arg; }

    printf " \$hoge = %s\n", &hoge; # (1)
    printf " \$foo  = %s\n", &foo;

    my $op = PerlOp->new;
    printf " \$op->foo = %s\n", $op->foo; # (2)
    printf " \$op->bar = %s\n", $op->bar;
}
{
    package PerlOp; use strict; use warnings;
    sub new { my $pkg = shift; bless{foo => 'foo', bar => 'bar'}, $pkg; }
    sub foo { my $self = shift; $self->{foo} = shift or return $self->{foo}; }
    sub bar { my $self = shift; $self->{bar} = shift || return $self->{bar}; }
    1;
}
__END__

=pod

出力 (ActivePerl v5.8.8 で確認) :
> perl test_perlop.pl
 $v1 = str
 $v2 = str
Use of uninitialized value in printf at test_perlop.pl line 23.
 $hoge = 
 $foo  = foo
Use of uninitialized value in printf at test_perlop.pl line 27.
 $op->foo = 
 $op->bar = bar

(1) と (2) の位置で Use of uninitialized value... と警告される

=cut

*1:行数節約スタイル。普段はこんな書き方しません。