我注意到在学习Ruby的过程中,每个方法的这两种用法都可以工作并产生相同的输出,我想知道Ruby是如何实现这一点的(以及我如何为自己的函数实现这一点):
my_array = [["hello","goodbye"],["picture","perfect"]]
my_array.each do |array|
puts array[0] + " " + array[1]
end
my_array.each do |first, second|
puts first + " " + second
end
我的理解是,当编写接受代码块的方法的定义时,yield方法被用来将参数传递给代码块并调用代码块。但是如何利用yield方法,使其根据所提供的代码块传递不同的参数呢?在示例中,当在块中使用两个参数(即first, second)时,yield方法传递单个数组元素,当在块中使用一个参数(即array)时,它传递数组本身。
each
和yield
在这里都没有做任何特别的事情,这就是块参数的工作方式。考虑这个简单的例子:
def f(x) yield x end
,现在我们可以看到发生了什么:
>> f([1,2]) { |a| puts a.inspect }
[1, 2]
>> f([1,2]) { |a, b| puts "#{a} - #{b}" }
1 - 2
>> f([1,2]) { |a, b, c| puts "#{a} - #{b} - #{c}" }
1 - 2 -
你会在赋值中看到类似的析构:
a, b = [1, 2]
你也可以显式地使用splat:
a, b = *[1, 2]
或者像这样:
def g(x) yield *x end
g([1, 2]) { |a, b| puts "#{a} - #{b}" }
假设块知道它将被给定的东西类型,因此块可以很好地解压缩参数。请注意,g
函数必须知道它的参数是可溅射的(即一个数组),但f
没有。f
很好地将"x
是什么类型的东西"逻辑放在对f
的调用中,g
将一半的逻辑隐藏在自己内部。当您在Hash上使用Enumerable方法时,差异变得明显:
{ :where => :is, :pancakes => :house? }.map { |k, v| ... }
Enumerable#map
不需要知道哈希在键/值两个元素数组中工作,它只是传递东西,让其他人去担心细节。