有人可以解释为什么这种迭代嵌套数据结构的方式确实有效吗?
我想创建这个数组
["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)