首页 > 解决方案 > 在 Ruby 中的 `do..end` 块和花括号中不同的 `puts` 行为

问题描述

我是一名经验丰富的开发人员,但也是一名 Ruby 新手。我正在阅读这个线程do..end vs curly brackets 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

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

我的"The new number is #{num}."琴弦怎么了?

标签: ruby

解决方案


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}." }

您已经传入putsmy_array.each它是一个枚举器和一个块,并且puts对传入其中的块不做任何事情,默认情况下所有方法也是如此。


推荐阅读