我用于Ruby运算符的引用是http://phrogz.net/programmingruby/language.html#table_18.4根据此引用和我所见过的其他引用,相等运算符优先于逻辑AND(&&
,而不是Ruby& #39; s and
)。
我有以下内容:
foo = nil
foo < 5 # NoMethodError: undefined method `<' for nil:NilClass
要检查我们的foo
:
foo && (foo < 5) # Note the parenthesis
但这有效:
foo && foo < 5 # why does this work?
由于运算符优先级,foo < 5
应该首先发生,导致AND之前的错误甚至可以被评估。我错过了什么吗?
答案 0 :(得分:3)
<
的优先级高于&&
,这会影响Ripper对您的表达式进行标记的方式。 标记后,进行评估。
在您的示例中,foo && foo < 5
被标记为两个表达式:
require 'ripper'
Ripper.sexp 'foo && foo < 5'
#=> [:program,
# [[:binary,
# [:vcall, [:@ident, "foo", [1, 0]]],
# :"&&",
# [:binary, [:vcall, [:@ident, "foo", [1, 7]]], :<, [:@int, "5", [1, 13]]]]]]
因此,解析器认为这在功能上等同于(foo) && (foo < 5)
,因为<
的优先级高于&&
。但是,由于Ruby对布尔运算使用短路评估,因此除非foo
计算为true
,否则它永远不会评估布尔表达式的右侧。
答案 1 :(得分:1)
以下帮助我理解了这个问题:
我可以重写
foo && foo < 5
作为
foo && foo.<(5)
现在更有意义。您可以从以下语句中获得相同的行为:
foo && foo.even?
您希望首先评估foo
,然后才foo.even?
。在foo
之前评估foo.<(5)
或在foo < 5
之前评估时,同样如此。
现在我试图想出一个例子,其中&&
和<
根据优先级表确实行为,但仍然没有成功。
答案 2 :(得分:0)
&&
和||
从左到右评估运营商。一旦知道陈述的真实性或虚假性,评估就会停止。永远不会评估foo < 5
。这就是第二种方法有效的原因。
此外,&&
和||
的优先级要高得多。它们与and
和or
不同。
答案 3 :(得分:0)
优先权是关于约束,不一定是操作的顺序。如果&&
的LHS评估为假,那么它根本不会对RHS进行评估。
Ruby旨在实现某种功能,所以我会给你一些Haskell来更详细地展示它:
true && x = x
false && x = false
由于左侧是假的,它永远不会继续计算右侧,所以它会崩溃。这是一些非常简单的懒惰评估。在第二行,x
的值是无关紧要的,所以它从不困扰计算它,因为它不需要。这是懒惰评价之美。 :)
答案 4 :(得分:0)
&&
短路,如果foo
为nil
或false
,则不会评估右侧。
解析器当然会按照下面的描述执行操作。但评估发生在那一步之后。
使用parser
gem来检查代码,我们可以得到一些关于Ruby解析器正在做什么的提示。
我有这两个文件:
foo1.rb
foo = nil
foo && foo < 5
AST表示:
(begin
(lvasgn :foo
(nil))
(and
(lvar :foo)
(send
(lvar :foo) :<
(int 5))))
foo2.rb
foo = nil
foo && (foo < 5)
AST表示:
(begin
(lvasgn :foo
(nil))
(and
(lvar :foo)
(begin
(send
(lvar :foo) :<
(int 5)))))
我不知道这是否澄清或混淆。