优先级规则

花括号内的块比 doend 中的块具有更高的优先级(precedence)。让我们看看这在实践中意味着什么。思考这两个例子:

  1. foo bar do |s| puts( s ) end
  2. foo bar{ |s| puts(s) }

这里,foobar 是方法。那么块传递给哪个方法?事实证明,do..end 块将被传递给最左边的方法 foo,而花括号中的块将被发送到最右边的方法 bar。这是因为花括号具有更高的优先级。思考这个程序…

precedence.rb
  1. def foo( b )
  2. puts("---in foo---")
  3. a = 'foo'
  4. if block_given?
  5. puts( "(Block passed to foo)" )
  6. yield( a )
  7. else
  8. puts( "(no block passed to foo)" )
  9. end
  10. puts( "in foo, arg b = #{b}" )
  11. return "returned by " << a
  12. end
  13. def bar
  14. puts("---in bar---")
  15. a = 'bar'
  16. if block_given?
  17. puts( "(Block passed to bar)" )
  18. yield( a )
  19. else
  20. puts( "(no block passed to bar)" )
  21. end
  22. return "returned by " << a
  23. end
  24. foo bar do |s| puts( s ) end # 1) do..end block
  25. foo bar{ |s| puts(s) } # 2) {..} block

这里 do..end 块的优先级较低,方法 foo 优先。这意味着 bardo..end 块都传递给 foo。因此,这两个表达式是等价的:

  1. foo bar do |s| puts( s ) end
  2. foo( bar ) do |s| puts( s ) end

另一方面,花括号块具有更高的优先级,因此它尝试立即执行并传递给第一个可能的接收方法 (bar)。然后将结果(即 bar 返回的值)作为参数传递给 foo;但这一次,foo 本身并没有收到块。因此,以下两个表达式是等效的:

  1. foo bar{ |s| puts(s) }
  2. foo( bar{ |s| puts(s) } )

如果你对这一切感到困惑,不要难过因为实际上并不是你一个人感到困惑!Ruby 块的行为远非透明的。潜在的歧义是由于在 Ruby 中参数列表周围的括号是可选的。从上面给出的替代版本中可以看出,当使用括号时,模糊性消失了。

提示…

一个方法可以使用 block_given? 方法测试它是否已经收到一个块。你可以在 precedence.rb 程序中找到相关示例。