[英]Can someone explain why this way of iterating a nested data structure does work?
我想創建這個數組
["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 中進行了一些思考和玩耍之后,我想知道為什么。 Ruby Doc for Arrays 的each
方法僅顯示具有一個項目變量的示例,如
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| p a }
["studies", {:freq=>11, :cap_freq=>0, :value=>11}]
["theory", {:freq=>9, :cap_freq=>1, :value=>11}]
讓我們更仔細地看一下。 定義:
enum = arr.each
#=> #<Enumerator: [["studies", {:freq=>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| p h }
{:freq=>11, :cap_freq=>0, :value=>11}
{:freq=>9, :cap_freq=>1, :value=>11}
這里我們不使用塊變量a
。 在這種情況下,我們可能會用局部變量_
或可能的_a
替換它:
arr.each { |_,h| p h }
arr.each { |_a,h| p h }
這提請注意未使用a
的事實, 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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.