【问题标题】:Does Ruby read "do" and "end" the same way as "{" and "}"?Ruby 读取“do”和“end”的方式是否与“{”和“}”相同?
【发布时间】:2014-07-29 03:42:41
【问题描述】:

在 Ruby 中编写块有两种不同的语法。有

do |something|
    ...
end

还有

{ |something|
    ...
}

我意识到这样做只是用括号替换关键字“do”和“end”。这让我想知道,当 Ruby 使用这两种语法时,它处理它们的方式是否有任何显着差异。或者 Ruby 是否以相同的方式处理它们,可以互换?如果它们完全可以互换,那么为什么要同时包含两者?

【问题讨论】:

  • @falsetru,这不是重复的。这个问题询问do...end{...} 是否(以及何时)可以互换。您链接的问题与样式有关,它询问当它们可互换时使用哪些。

标签: ruby syntax lambda block


【解决方案1】:

不,它没有。作为 Ruby 的一般规则,如果两件事看起来很相似,那么您可以打赌它们之间存在细微的差别,这使得它们中的每一个都是独一无二且必要的。

{} 并不总是充当块分隔符的角色。当它们不充当块分隔符时(例如在构造哈希时,{ a: 1, b: 2 }),它们不能被do...end替换。但是当花括号确实分隔了一个块时,它们几乎总是可以替换为do ... end。但要小心,因为有时这会改变你陈述的意思。这是因为{ ... } 具有更高的优先级,它们比do ... end 绑定更紧密:

puts [ 1, 2, 3 ].map { |e| e + 1 }         # { ... } bind with #map method
2
3
4
#=> nil

puts [ 1, 2, 3 ].map do |e| e + 1 end      # do ... end bind with #puts method
#<Enumerator:0x0000010a06d140>
#=> nil

至于相反的情况,do ... end 在块分隔符的作用下不能总是用大括号代替。

[ 1, 2, 3 ].each_with_object [] do |e, obj| obj << e.to_s end  # is possible
[ 1, 2, 3 ].each_with_object [] { |e, obj| obj << e.to_s end   # invalid syntax

在这种情况下,您必须将有序参数括起来:

[ 1, 2, 3 ].each_with_object( [] ) { |e, obj| obj << e.to_s }  # valid with ( )

这些句法规则的结果是你可以这样写:

[ 1, 2, 3 ].each_with_object [ nil ].map { 42 } do |e, o| o << e end
#=> [ 42, 1, 2, 3 ]

上面还演示了{}不能被do/end替换的情况:

[ 1, 2, 3 ].each_with_object [ nil ].map do 42 end do |e, o| o << e end
#=> SyntaxError

-&gt; 的 Lambda 语法略有不同,因为它同样接受两者:

foo = -> x do x + 42 end  # valid
foo = -> x { x + 42 }     # also valid

此外,doend 本身并不总是分隔块。特别是这个

for x in [ 1, 2, 3 ] do
  puts x
end

还有这个

x = 3
while x > 0 do
  x -= 1
end

还有这个

x = 3
until x == 0 do
  x -= 1
end

表面上包含do ... end 关键字,但它们之间并没有真正的块。它们里面的代码并没有引入新的范围,它只是用于重复一些语句的语法。这里不能使用大括号,语法可以不写do,如:

for x in [ 1, 2, 3 ]
  puts x
end

# same for while and until

对于{...}do...end 分隔符可互换的情况,使用哪个分隔符的选择取决于编程风格,并且是extensively discussed in another question(来自我在这里举了我的一个例子,我接受了谁的答案)。在这种情况下,我个人的偏好是强调可读性而不是严格的规则。

【讨论】:

  • 哇!非常感谢您提供如此周到和深思熟虑的答案!我要花一些时间来消化这个。我可能会问几个后续问题。
  • Rubyist 需要时间来弄清楚这一切。
【解决方案2】:

一般{ }块语法用于单行块,do ... end语法用于多行块。两者之间的一个区别是您的参数必须在花括号块的括号内。示例:

some_method(param1, param2) { ... }

some_method param1, param2 do
   ...
end

【讨论】:

  • "123".gsub /./ do |s| (s.to_i + 1).to_s end =&gt; "234",但"123".gsub /./ { |s| (s.to_i + 1).to_s } 会引发语法错误。另一方面,[1,2,3].each_with_object [] { |e,a| a &lt;&lt; e + 1 } =&gt; [2, 3, 4]。知道为什么吗?
  • @CarySwoveland 不知道,在块规范中也找不到。
  • 因为 Ruby 解析器需要一些提示来判断 { 是散列的开头还是块的开头。 Ruby 解析器尝试将 some_method p1, p2, {...} 解析为哈希参数,而不是阻塞;另一方面,它将some_method {...} 视为块参数。
  • 感谢@huocp 的解释。这是有道理的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-08
  • 2020-10-22
  • 2020-10-21
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多