【问题标题】:Why can't I assign @b || @c to @a in Perl?为什么我不能分配@b ||在 Perl 中 @c 到 @a?
【发布时间】:2010-11-24 08:48:14
【问题描述】:

我想对@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:在问题的范围之外,我不建议使用三元运算符的原因是我的 @b 实际上是一个临时的(函数调用结果)。

【问题讨论】:

  • “真的是这个意思”这行告诉你原因。
  • 对我来说,这更像是什么而不是为什么。但我有偏见。
  • 我宁愿认为评论“真的意味着这个”应该改为“真的意味着这个”。就这样,我期待 scalar() 更改将@a 设置为等于@b - 即成为作者真正想要使用的东西 - 显然情况并非如此。相反,第一行实际上意味着第二行。他们都得到相同的结果,这不是我们想要的答案。

标签: perl grammar


【解决方案1】:

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

这样做的原因是要弄清楚这个论点是否为真。布尔上下文,作为标量上下文的一种特殊情况,将其强制转换为标量上下文。


来自perldoc perlop"C-style-Logical-Or"

二进制“||”执行短路逻辑或运算。也就是说,如果左操作数为 true,则甚至不计算右操作数。 ...


来自perldoc perldata"Scalar values"

....布尔上下文只是一种特殊的标量上下文,它不会执行到字符串或数字的转换。

【讨论】:

  • 您已经得到了我正在寻找的答案。我在 perldata 中发现了这一点:“布尔上下文只是一种特殊的标量上下文,它不会执行到字符串或数字的转换。”虽然原因(“需要弄清楚它是否属实”)对我来说确实有点可疑。
  • 值可以为真或假,但聚合不能。对于聚合,“真”通常意味着“非空”。当您在标量上下文中评估聚合时,如果聚合为空,则会得到一个值为 false 的值,否则为 true。
  • @Michael:虽然你所说的与周围的解释一致,但在我看来,它确实有点神奇(即无法解释),并且与 perlsyn 的关于真/假的段落相矛盾,这主要是说: “空列表是假的,其他都是真的”。我需要接受这个事实的相关信息是由布拉德带来的:“布尔评估强制转换为标量。”来自 perlsyn,IMO 的重要演绎。
  • 关键字是'is' - 列表不是布尔值,而是列表值,因此说“空列表为假”实际上是“空列表”的简写,当转换为布尔值,为 false'。
【解决方案2】:

在 perlop 中,在您引用的部分前几段:

二进制“||”执行短路逻辑或运算。那是, 如果左操作数为真,则甚至不计算右操作数。 标量或列表上下文向下传播到正确的操作数,如果它是 评估。

这并没有明确说明列表上下文不会传播到左操作数,而是 perlop 状态的顶部:

除了极少数例外,这些都对标量值进行操作 只有,不是数组值。

所以我们可以假设传播到正确操作数的列表上下文是 规则的例外,并且缺乏关于上下文的任何陈述 左操作数意味着适用一般规则。

【讨论】:

    【解决方案3】:

    因为||在标量上下文中评估左侧,就像 ?: 的测试参数一样。 如果不能使用三元,使用函数:

    sub or_array (\@\@) {
      return @{$_[0]} if ( scalar @{$_[0]} );
      return @{$_[1]};
    }
    
    @a = or_array(@b, @c);
    

    【讨论】:

    • 毫无疑问 perl6 中有一个神秘的运算符来处理这个问题。 :)
    【解决方案4】:

    如果你不能直接使用条件运算符,你可以很容易地使用稍微不太简洁的:

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

    根据上述答案,您的代码不起作用,因为|| 左侧被评估的逻辑上下文是标量上下文,因此@b 实际上变成scalar(@b),这就是分配给@ 987654325@.

    【讨论】:

      猜你喜欢
      • 2011-12-14
      • 2022-07-23
      • 2011-08-10
      • 1970-01-01
      • 2011-05-30
      • 2014-03-29
      • 2014-01-15
      • 1970-01-01
      • 2021-10-06
      相关资源
      最近更新 更多