Почему я не могу назначить @b || @c в @a в Perl? - PullRequest
12 голосов
/ 31 августа 2009

Я хотел бы выполнить некоторую запутанную вариацию назначения @a = @b || @c, намереваясь взять @b, если он не пустой (следовательно, истинный в логическом смысле), @c в противном случае. Документация явно говорит мне, что я не могу. (И это тоже верно!)

Операторы "||", "//" и "&&" возвращают последнее оцененное значение (в отличие от C "||" и "&&", которые возвращают 0 или 1).

[...]

В частности, это означает, что вы не должны использовать это для выбора между двумя агрегатами для назначения:

@a = @b || @c;              # this is wrong
@a = scalar(@b) || @c;      # really meant this
@a = @b ? @b : @c;          # this works fine, though

К сожалению, на самом деле это не говорит мне, почему.

Я ожидал, что произойдет следующее:

  • @a = - это присвоение массива, вызывающее контекст списка справа.
  • @b || @c - правая часть, которая должна оцениваться в контексте списка.
  • || - логическое короткое замыкание в стиле C или. Он оценивает слева направо (при необходимости) и распространяет контекст.
  • @b оценивается в контексте списка. Если true (, т.е. , не пусто), оно возвращается.
  • если нет, @c также вычисляется в контексте списка и возвращается.

Очевидно, мое предпоследнее утверждение неверно. Зачем? И, что более важно, какая часть документации или источников объясняет это поведение?

PS: из сферы действия вопроса я воздерживаюсь от предложенного в документации использования тернарного оператора, потому что мой @b фактически является временным (результат вызова функции).

Ответы [ 4 ]

7 голосов
/ 31 августа 2009

В perlop, всего несколько абзацев перед цитируемым разделом:

Binary "||" performs a short-circuit logical OR operation.  That is,
if the left operand is true, the right operand is not even evaluated.
Scalar or list context propagates down to the right operand if it is
evaluated.

Это явно не означает, что контекст списка распространяется не на левый операнд, а на верхнюю часть состояний perlop:

With very few exceptions, these all operate on scalar values
only, not array values.

, поэтому мы можем предположить, что контекст списка, распространяющийся к правому операнду, является исключение из правила и отсутствие каких-либо утверждений о контексте левый операнд подразумевает применение общего правила.

7 голосов
/ 31 августа 2009

Оператор логического или ("||") оценивает свой левый аргумент в скалярном контексте.

Причина этого заключается в том, чтобы выяснить, является ли аргумент верным. Логический контекст, являющийся частным случаем скалярного контекста, переводит его в скалярный контекст.


С perldoc perlop "C-style-Logical-Or"

Двоичный "||" выполняет операцию логического ИЛИ короткого замыкания. То есть, если левый операнд равен true , правый операнд даже не оценивается. ...


С perldoc perldata «Скалярные значения» :

.... Логический контекст - это просто особый вид скалярного контекста, в котором преобразование в строку или число никогда не выполняется.

2 голосов
/ 31 августа 2009

Это потому что || оценивает левую часть в скалярном контексте, как и тестовый аргумент?:. Если вы не можете использовать троичный, используйте функцию:

sub or_array (\@\@) {
  return @{$_[0]} if ( scalar @{$_[0]} );
  return @{$_[1]};
}

@a = or_array(@b, @c);
0 голосов
/ 31 августа 2009

Если вы не можете использовать условный оператор напрямую, вы можете легко использовать чуть менее краткий:

my $ref_b = [ @b ]; # Ideally, just return an arrayref from your function
my @a = @$ref_b ? @$ref_b : @c;

Согласно ответу выше, ваш код не работает, так как логический контекст, в котором оценивается левая часть ||, является скалярным контекстом и, таким образом, @b фактически становится scalar(@b), и именно это присваивается @a.

...