Подтвердить что ты не робот

Почему я не могу назначить @b || @c to @a в Perl?

Я хотел бы выполнить некоторую запутанную вариацию назначения @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 (т.е. Не пусто), он возвращается.
  • if not, @c также оценивается в контексте списка и возвращается.

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

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

4b9b3361

Ответ 1

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

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


Из perldoc perlop "C-style-Logical- Или"

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


Из perldoc perldata "Скалярные значения" :

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

Ответ 2

В 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.

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

Ответ 3

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

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

@a = or_array(@b, @c);

Ответ 4

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

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

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