Почему returnarray возвращается в скалярном контексте при вызове foo() || умереть?
Я только что потратил кучу времени, отлаживая проблему, которую я проследил до wantarray()
. Я переусердствовал до этого теста. (Игнорируйте тот факт, что $!
не будет иметь никакой полезной информации в этом сценарии). Я хотел бы знать, почему wantarray
не считает, что он вызван в контексте LIST во втором примере:
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
{
my ( $one, $two ) = foo();
is( $one, 'a', 'just foo' );
is( $two, 'b', 'just foo' );
}
{
my ( $one, $two ) = foo() || die $!;
is( $one, 'a', '|| die' );
is( $two, 'b', '|| die' );
}
done_testing();
sub foo {
return wantarray ? ( 'a', 'b' ) : 'bar';
}
Выход этого теста:
$ prove -v wantarray.pl
wantarray.pl ..
ok 1 - just foo
ok 2 - just foo
not ok 3 - || die
not ok 4 - || die
1..4
# Failed test '|| die'
# at wantarray.pl line 15.
# got: 'bar'
# expected: 'a'
# Failed test '|| die'
# at wantarray.pl line 16.
# got: undef
# expected: 'b'
# Looks like you failed 2 tests of 4.
Dubious, test returned 2 (wstat 512, 0x200)
Failed 2/4 subtests
Test Summary Report
-------------------
wantarray.pl (Wstat: 512 Tests: 4 Failed: 2)
Failed tests: 3-4
Non-zero exit status: 2
Files=1, Tests=4, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.02 cusr 0.00 csys = 0.06 CPU)
Result: FAIL
Ответы
Ответ 1
Потому что он не вызывается в контексте списка. ||
накладывает скалярный контекст на его левую сторону, а его левая сторона в этом случае является выражением foo()
.
Вместо этого вы должны написать
my ( $one, $two ) = foo() or die $!;
Оператор or
связывается даже более свободно, чем оператор присваивания, поэтому теперь его LHS является всем выражением my ($one, $two) = foo()
, а контекст foo
определяется оператором присваивания списка, и все довольны.
Ответ 2
Причина связана с приоритетом оператора ||
. Ваше выражение в основном анализируется следующим образом:
my ( $one, $two ) = ( foo() || die $! );
В этом случае ||
помещает свои операнды в скалярный контекст.
С другой стороны, если вы измените ||
на or
, который имеет гораздо более низкий приоритет, ваши тесты пройдут.
Ответ 3
Логический или (||
) - скалярный оператор, поэтому его использование заставит оценку foo()
стать скалярным контекстом.
Попробуйте следующее:
my @a = 10 .. 100;
print(@a || 2), "\n";
# prints 91
Вы ожидаете, что это напечатает элементы в @a
, если это не так, потому что массив был оценен в скалярном контексте.