簡體   English   中英

有人可以解釋為什么這種迭代嵌套數據結構的方式有效嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM