【问题标题】:Precedence of to-block unary `&`阻止一元`&`的优先级
【发布时间】:2018-11-23 03:58:21
【问题描述】:

考虑以下 Ruby 代码:

[1,3].any? &:even? || true
# => false
[1,3].any? &nil || :even?
# => false
[1,3].any? &nil || :odd?
# => true

因此,Boolean-or || 似乎比 to-proc 一元 & 具有更高的优先级。我没想到会这样。是这样吗?它是否记录在任何地方?

【问题讨论】:

  • 是的,我知道解决方案只是在第一个示例中将括号粘贴在 &:even 周围。
  • 一个更清晰的例子:def s;end; s &false || nil。返回nil,但我预计会出现 TypeError
  • 因为& 基本上使参数表现得像一个块,它似乎与the lowest between all other operators 的块具有相同的优先级。
  • || 运算符非常非常具有侵略性。
  • @potashin 如果这是真的,那么[1,3].any? &:even? or true 将返回false 因为or 的优先级高于块,所以[1,3].any? &(:even? or true) #=> false 但一元& 的优先级高于or 和对 any? 的方法调用也是如此,因此评估是 [1,3].any?(&(:even?)) or true #=> true

标签: ruby operator-precedence


【解决方案1】:

这就是(被错误地诽谤的)andor 关键字的用途。你应该把它写成

[1,3].any? &:even? or true

至于为什么会发生这种情况——我找不到这方面的文档——但我认为它实际上更多地与可选括号和一元 & 的限制有关。

一元 & 是特殊的。像~ 这样的“普通”运算符本质上是方法调用的语法糖;你可以把它们放在你想要的任何地方。但是&只允许在方法参数中,即使这样也只能在最后。

foo x, &bar
# NameError, determined at runtime because it has to see if any of these names are defined
foo &bar, x
# SyntaxError! Didn't even make it past the parser

y = bar
# NameError
y = &bar
# SyntaxError!

当你在方法调用中去掉括号时,它会吞掉几乎所有的东西,只在 if/unless/and/or 等超低优先级的东西上停止。

foo bar baz if true
# same as
foo(bar(baz)) if true

所以你的例子相当于

[1,3].any?(&:even? || true)

现在,如果& 在某种程度上具有高优先级,这要么是在运行时评估的完全正常值true,要么是高度受限的特殊语法构造&:even?。在运行时发现语法错误并不是很好,所以我猜开发人员选择了一种简单的方法来解决它:使& 超低优先级。这样解析器可以只验证语法规则并忽略块参数本身(必须在运行时评估)。

【讨论】:

  • 我需要在这里消化最后几段。我只是澄清一下andor 不是我想要的——我提炼出了一个实际上更像if my_array.any? &:predicate? || other_condition 的例子,它仅基于any? 触发或不触发
  • 这与块无关,只是可选的括号咬你:if foo bar || bazif foo(bar || baz)
  • @Chowlett 和or 也将修复该错误:if my_array.any? &:predicate or other_condition 符合您的预期。虽然我更喜欢括号,因为这看起来有点混乱。
  • 您不应该将andor 用于布尔组合,只能使用流控制。是的,解决方法显然是添加括号。当然,关于方法调用的贪婪是相关的。
  • @Max 可操作和 (&) 是一个方法调用,但优先级高于逻辑或 (||) [参见我的帖子中的示例]。所以这似乎不属于“......它几乎吞没了所有东西,只停留在超低优先级的东西上,比如 if/unless/and/or”。否则我会预计像true & nil || :even? 这样的东西会被解析为true.&(nil || :even?) #=> true,但你会得到true.&(nil) || :even? #=> :even?
【解决方案2】:

& 前面没有一个对象(一元)并且紧随其后的是其他任何东西(在这种情况下为&nil)。它将被解析为尝试的to_proc 方法调用,否则它将被视为接收方的方法调用,例如

 nil&nil
 #=> false  

相当于nil.&(nil)

所以在你的情况下([1,3].any? &nil || :even?)它被解析为[1,3].any?(&(nil || :even?)),因为&to_proc方法调用)的优先级低于逻辑||,并且需要知道nil || :even?的结果继续。

但是,可操作的 and (&) 需要一个接收器(不是一元的),但确实比逻辑 or (||) 具有更高的优先级,例如[1,3].any? &nil&nil || :even? 将评估为

[1,3].any?(&(nil.&(nil) || :even?)
#=> false
# Or
[1,3].any?(&(nil & nil || :even?)
#=> false

奇怪的是,显然操作和 (&) 的优先级高于逻辑或 (||)

更多例子

true & nil || :even? 
#=> :even?

true & :even? 
#=> true

[1,3].any? &true&true || :even?
#=> TypeError: wrong argument type TrueClass (expected Proc)

# Okay no problem

class TrueClass
  def to_proc
    ->(*_) { true } 
  end
end 

[1,3].any? &true&true || :even?
#=> true
[1,3].any? &true || :even?
#=> true

更奇怪的是,一元 & 允许 nil 但基本上从 block_given? 中忽略它,但任何其他未实现 to_proc 的对象都会引发 TypeError

def no_block
  block_given? ? yield('Yes') : 'No'
end

no_block &nil
#=> "No"

no_block &false
#=> TypeError: wrong argument type FalseClass (expected Proc)

【讨论】:

  • 我认为它与nil 一起工作的原因是因为它可能是&var,然后需要有某种方法来设置var,使得block_given? 为假。一个特殊的例外,但却是必要的。
  • @Max 我同意这是一个奇怪但必要的邪恶,&nil == nil 在方法参数的上下文中def block_is_nil?(&block); block == nil; end 这样block_is_nil?(&nil) #=> true
猜你喜欢
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
相关资源
最近更新 更多