【问题标题】:differing `puts` behavior inside `do..end` blocks and curly braces in Ruby [duplicate]Ruby中`do..end`块和花括号中的不同`puts`行为[重复]
【发布时间】:2020-10-21 10:18:48
【问题描述】:

我是一名经验丰富的开发人员,但也是一名 Ruby 新手。我正在阅读这个线程do..end vs curly braces for blocks in Ruby 并学到了一些关于何时使用大括号以及何时使用do .. end 块的好东西。提示之一是当您遇到副作用时使用 do .. end,当您担心返回值时使用 {}。因此,我正在尝试使用枚举器教程中的一些示例:

irb(main):001:0> my_array = [1, 2]
=> [1, 2]
irb(main):002:0> my_array.each {|num| num *= 2; puts "The new number is #{num}."}
The new number is 2.
The new number is 4.
=> [1, 2]
irb(main):003:0> my_array.each do |num| num *= 2; puts "The new number is #{num}." end
The new number is 2.
The new number is 4.
=> [1, 2]

等一下。我认为do..end 块返回一个枚举器对象?它看起来像一个数组。让我们检查一下:

irb(main):004:0> puts my_array.each {|num| num *= 2; puts "The new number is #{num}."}
The new number is 2.
The new number is 4.
1
2
=> nil
irb(main):005:0> puts my_array.each do |num| num *= 2; puts "The new number is #{num}." end
#<Enumerator:0x000055967e53ac40>
=> nil

好的,它是一个枚举器。但是在第 005 行的循环中,puts 调用的输出发生了什么变化? {} 具有预期的副作用,但 do..end 块没有,这似乎违反了该经验法则。

我的"The new number is #{num}." 字符串发生了什么事?

【问题讨论】:

  • this 回答你的问题了吗?
  • 它解释了第一部分,我可能应该把它排除在我的问题之外,因为我想我明白了。但我更关心puts "The new number is #{num}." 的输出去了哪里。

标签: ruby


【解决方案1】:

do...end{} 对于方法块在语义上是 100% 等效的。它们唯一的区别是它们的解析优先级,因此它们的评估方式不同

要真正理解这种区别,首先要了解几件事。

Ruby 允许您调用不带括号的方法:

my_object.my_method my_arg

# so my_arg could actually be a method! Let's put parens in to show that:
my_object.my_method(my_arg())

Ruby 中的块是方法参数——传入闭包的语法(在父作用域中起作用的特殊关键字除外)。以下两个块是等价的:

[1, 2, 3].map { |x| 2 * x }

# split out into two lines
double = ->(x) { 2 * x }  # shorthand for `lambda { |x| 2 * x }`
[1, 2, 3].map(&double)

好的,知道了这些,让我们来看看{}do...end之间的区别:

my_method [1, 2, 3].map { |x| 2 * x }

my_method([1, 2, 3].map { |x| 2 * x }) # parses like this


my_method [1, 2, 3].map do |x| 2 * x end

my_method([1, 2, 3].map) do |x| 2 * x end # parses like this

my_method([1, 2, 3].map) { |x| 2 * x }    # in other words

{}do...end 具有更高的优先级,立即与其左侧的方法关联。 do...end 的优先级较低,将与 my_method 关联,后者通过 [1, 2, 3].map 和块作为参数。

这意味着,您在上面所做的是:

puts(my_array.each) { |num| num *= 2; puts "The new number is #{num}." }

您已将my_array.each 传递给puts,它是一个枚举器和一个块,而puts 对传入它的块没有任何作用,默认情况下所有方法也是如此。

【讨论】:

  • 感谢您,布置得如此清晰明了。我来自 Common Lisp 的世界,所以我可以告诉你,我很高兴得知 Ruby 有 lambdas。我期待了解关于默认情况下忽略块参数的方法的最后一点,因为我完成了我的平板 ATM 上的所有教程。干杯。
  • @coby101:这与 Common Lisp 没有区别。如果您将 lambda 传递给函数但该函数从不调用 lambda,则不会发生任何事情。 puts 对块没有用处,所以它永远不会调用它。
  • @coby101 你可能会喜欢这篇文章:randomhacks.net/2005/12/03/why-ruby-is-an-acceptable-lisp
  • 谢谢,@Kache。这是一篇很有启发性的读物,在我看到它们有多长和多密集之前,我几乎被所有的 cmets 所吸引!我在 comp.lang.lisp 的日子里记得很多相同的问题和观点。写于 15 年前,我得到的印象是 Ruby 已经成长和老化了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 1970-01-01
  • 2016-04-10
  • 1970-01-01
  • 2011-10-06
相关资源
最近更新 更多