在Ruby中,为什么当使用“do”和“end”时,方法调用无法被视为一个单元?

9
以下问题与问题“Ruby Print Inject Do Syntax”有关。我的问题是,我们能坚持使用doend并使其与putsp一起使用吗?
这是有效的:
a = [1,2,3,4]

b = a.inject do |sum, x|
  sum + x
end
puts b   # prints out 10

那么,可以这样说吗,inject是Array对象的一个实例方法,这个实例方法接受一段代码块,然后返回一个数字。如果是这样的话,那么它与调用函数或方法并获取返回值没有任何区别:

b = foo(3)
puts b

或者

b = circle.getRadius()
puts b

在上述两种情况下,我们可以直接说
puts foo(3)
puts circle.getRadius()

因此,不能通过以下两种方式直接使其正常工作:

a = [1,2,3,4]

puts a.inject do |sum, x|
  sum + x
end

但它会提供

ch01q2.rb:7:in `inject': no block given (LocalJumpError)
        from ch01q2.rb:4:in `each'
        from ch01q2.rb:4:in `inject'
        from ch01q2.rb:4

使用括号( )来分组方法调用也无效:

a = [1,2,3,4]

puts (a.inject do |sum, x| 
        sum + x   
      end)

这将会得到:

ch01q3.rb:4: syntax error, unexpected kDO_BLOCK, expecting ')'
puts (a.inject do |sum, x|
                 ^
ch01q3.rb:4: syntax error, unexpected '|', expecting '='
puts (a.inject do |sum, x|
                          ^
ch01q3.rb:6: syntax error, unexpected kEND, expecting $end
      end)
         ^

最终,以下版本可以正常工作:
a = [1,2,3,4]

puts a.inject { |sum, x|
    sum + x
}

但是为什么在先前的示例中使用( )进行方法调用的分组不起作用呢?如果程序员坚持使用doend,能否使其起作用?


1
“inject” 实际上是一个实例方法。 - Matthew Flaschen
啊,没错,调用实例的值/属性的方法应该是一个实例方法。 - nonopolarity
谢谢你,Matthew。问题已在题目中得到纠正。 - nonopolarity
我不知道为什么你写了这样一个庞大的哲学问题,当实际上问题只是一个优先级的问题。 - horseyguy
2个回答

15
从(非官方的)Ruby语法中,我们可以看到 puts (...) 中的 (...) 必须是CALL_ARGS,而不直接简化为STMT。但是,它们可以简化为'(' COMPSTMT ')' 。通过添加额外的括号,您可以使用do ... end
a = [1,2,3,4]

puts ((a.inject do |sum, x| 
         sum + x   
       end))

1
它确实有效。对我来说,可能会感觉有些奇怪,因为Ruby的括号有时是可选的。而且,如果一对不起作用,添加另一对括号可能会让人感到困惑。 - nonopolarity

7
这里的问题不仅仅是你的括号:主要是在括号之后,在 puts 前面加上空格。
代码如下:
a = [1,2,3,4]

puts (a.inject do |sum, x|
             sum + x
                    end)

我们看到了你在问题中列出的语法错误。
如果你删去puts后面的空格,
a = [1,2,3,4]

puts(a.inject do |sum, x|
             sum + x
                    end)

正如预期的那样,prints out 10

最后,使用 puts ((a.inject... 带上空格和双括号也会输出 10,但是通过运行 ruby -cw XXX.rb 告诉我们:

a.rb:5: warning: (...) interpreted as grouped expression

Syntax OK

ruby -cw 用于检查语法并打开所有警告。当启用 -cw 时,您将收到有关可疑括号和分组的警告。我更习惯看到的错误是“不要在参数括号前放置空格”——所以也不要这样做!

最后,a.inject do 没有括号失败,但 a.inject { 却可以工作,原因是大括号比 do/end 的优先级高。非常粗略地说,你可以说 p a.map { foo } 等同于 p(a.map do foo end);而 p a.map do foo end 等同于 (p a.map) do foo end,当然不带块参数。

另请参见Ruby快速参考关于块的部分(特别是最后两行):

块,闭包和过程

块/闭包

  • 块必须跟随方法调用:

invocation do ... end

invocation { ... }

  • 块记住它们的变量上下文,并且是完整的闭包。
  • 块通过 yield 调用,并可以传递参数。
  • 大括号形式具有更高的优先级,如果没有括号,将绑定到最后一个参数。
  • do/end形式具有较低的优先级,即使没有括号也会绑定到调用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接