有人可以解释为什么这种迭代嵌套数据结构的方式确实有效吗?

我想创建这个数组

["studies", "theory", "form", "animal", "basic", "processes"] 

从以下嵌套数据结构(存储为sorted_hash ):

 [["studies", {:freq=>11, :cap_freq=>0, :value=>11}], ["theory", {:freq=>9, :cap_freq=>1, :value=>11}], ["form", {:freq=>9, :cap_freq=>1, :value=>11}], ["animal", {:freq=>12, :cap_freq=>0, :value=>12}], ["basic", {:freq=>10, :cap_freq=>1, :value=>12}], ["processes", {:freq=>13, :cap_freq=>0, :value=>13}]] 

我把它混淆为哈希并编写以下代码来实现我的任务:

 sorted_hash.each do |key,value| array.push key end 

我真的得到了我想要的东西。 但经过一些思考和在Pry玩耍后,我想知道为什么。 each方法Ruby Doc for Arrays仅显示带有一个项变量的示例,如

each { |item| block } → ary

但我使用两个变量,就像我为哈希所做的那样。 Ruby会尝试匹配给定的项变量,在这种情况下成功,因为第二级数组的长度为2吗? 这样做是否值得推荐? 有更多惯用的方法吗?

答案来自于在Ruby中实现“并行分配”的方式。

你可能知道:

 a,b,c = 1,2,3 a #=> 1 b #=> 2 c #=> 3 a,b,c = [1,2,3] a #=> 1 b #=> 2 c #=> 3 a,b = [1,2,3] a #=> 1 b #=> 2 a,*b = [1,2,3] a #=> 1 b #=> [2, 3] *a,b = [1,2,3] a #=> [1, 2] b #=> 3 a,(b,c) = [1,[2,3]] a #=> 1 b #=> 2 c #=> 3 a,(b,(c,d)) = [1,[2,[3,4]]] a #=> 1 b #=> 2 c #=> 3 d #=> 4 

最后两个例子使用“消歧”,有些人更喜欢称之为“分解”。

现在让我们看看它如何应用于块值变量的赋值。

假设:

 arr = [["studies", {:freq=>11, :cap_freq=>0, :value=>11}], ["theory", {:freq=>9, :cap_freq=>1, :value=>11}]] 

我们执行:

 arr.each { |a| pa } ["studies", {:freq=>11, :cap_freq=>0, :value=>11}] ["theory", {:freq=>9, :cap_freq=>1, :value=>11}] 

让我们更仔细地看一下。 限定:

 enum = arr.each #=> #11, :cap_freq=>0, :value=>11}], # ["theory", {:freq=>9, :cap_freq=>1, :value=>11}]]:each> 

第一个元素传递给块并分配给块变量v

 v = enum.next #=> ["studies", {:freq=>11, :cap_freq=>0, :value=>11}] 

我们可能更喜欢使用带有两个块变量的并行赋值(在enum.rewind之后重置枚举器):

 a,h = enum.next a #=> "studies" h #=> {:freq=>11, :cap_freq=>0, :value=>11} 

这允许我们写(例如):

 arr.each { |a,h| ph } {:freq=>11, :cap_freq=>0, :value=>11} {:freq=>9, :cap_freq=>1, :value=>11} 

这里我们不使用块变量a 。 在这种情况下,我们可以用局部变量_或可能的_a替换它:

 arr.each { |_,h| ph } arr.each { |_a,h| ph } 

这引起了人们对a未被使用并且可能有助于避免错误的事实的关注。 关于错误,假设我们想要:

 [[1,2],[3,4]].map { |a,b| puts 1+b } #=> [3,5] 

但无意中写道:

 [[1,2],[3,4]].map { |a,b| puts a+b } #=> [3,7] 

执行得很好(但产生不正确的结果)。 相比之下,

 [[1,2],[3,4]].map { |_,b| puts a+b } #NameError: undefined local variable or method 'a' 

告诉我们有一个问题。

下面是一个更详细的示例,说明了在具有并行赋值和消歧的块中可以执行的操作。 鉴于:

 h = { :a=>[1,2], :b=>[3,4] } 

假设我们希望获得:

  { :a=>3, :b=>7 } 

一种方法如下:

 h.each_with_object({}) { |(a,(b,c)),g| g[a] = b+c } => {:a=>3, :b=>7} 

那是因为Ruby方便地让你这样做:

 [[1,2,3], [4,5,6]].each {|x,y,z| puts "#{x}#{y}#{z}"} # 123 # 456 

所以基本上, each为块生成一个数组元素,并且因为Ruby块语法允许通过提供参数列表将数组元素“扩展”到它们的组件,所以它可以工作。

你可以在这里找到更多带有块参数的技巧。

顺便说一句,不是自己创建一个数组并调用push ,你可以简单地执行以下操作,因为map返回一个数组:

 sorted_hash.map(&:first)