to-block unary`&`的优先顺序

时间:2018-06-13 14:13:53

标签: ruby operator-precedence

考虑以下Ruby代码:

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

所以看起来布尔 - 或||的优先级高于to-proc一元&。我没想到这一点。这是正确的,是否记录在任何地方?

2 个答案:

答案 0 :(得分:4)

这是{错误诽谤的} 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 /和/或。

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

所以你的例子相当于

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

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

答案 1 :(得分:0)

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

 nil&nil
 #=> false  

相当于nil.&(nil)

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

然而,操作和(&)需要一个接收器(不是一元),但确实比逻辑或(||)具有更高的优先级,例如: [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) }

{{1}}