13

我想执行一些复杂的@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在列表上下文中进行评估。如果为真(非空),则返回。
  • 如果不是,@c也将在列表上下文中进行评估并返回。

显然,我的倒数第二个陈述是错误的。为什么?而且,更重要的是,文档或来源的哪一部分解释了这种行为?

PS:在问题的范围之外,我避免使用三元运算符的文档建议的原因是 my@b实际上是一个临时的(函数调用结果)。

4

4 回答 4

7

逻辑或运算符 ( " ||" ) 在标量上下文中评估其左侧参数。

The reason it does this is to figure out if the argument is true. Boolean context, being a special case of scalar context, forces it into scalar context.


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

Binary "||" performs a short-circuit logical OR operation. That is, if the left operand is true, the right operand is not even evaluated. ...


From perldoc perldata "Scalar values":

.... The Boolean context is just a special kind of scalar context where no conversion to a string or a number is ever performed.

于 2009-08-31T14:21:06.630 回答
7

In perlop, just a few paragraphs before the section you quote:

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.

This doesn't explicitly state that list context does not propagate to the left operand, but the top of perlop states:

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

so we can assume that list context propagating to the right operand is the exception to the rule, and the lack of any statement about the context of the left operand implies the general rule applies.

于 2009-08-31T14:23:10.667 回答
2

It's because || evaluates the left side in scalar context, as does the test argument of ?: . If you can't use the ternary, use a function:

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

@a = or_array(@b, @c);
于 2009-08-31T14:26:07.620 回答
0

If you can't use the conditional operator directly, you can easily use the slightly-less-terse:

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

As per above answer, your code does not work since the logical context that the left side of || is evaluated in is a scalar context and thus @b actually becomes scalar(@b) and that is what gets assigned to @a.

于 2009-08-31T14:35:58.213 回答