2017-11-18 254 views
1

試圖在這裏理解這個問題。我創建了我自己的散列#合併名爲hash#my_merge的可以在proc中使用的版本。我想知道是什麼做的:試圖理解哈希的實現#與Procs合併

self.each do |k,v| 
    newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v 
end 

它看起來像一個三元操作,但newhash [K] =散列[K]是不是一個真正的/虛假陳述?提示和問題的其餘部分如下:

class Hash 
# Hash#merge takes a proc that accepts three arguments: a key and the two 
# corresponding values in the hashes being merged. Hash#merge then sets that 
# key to the return value of the proc in a new hash. If no proc is given, 
# Hash#merge simply merges the two hashes. 
# 
# Write a method with the functionality of Hash#merge. Your Hash#my_merge 
method 
# should optionally take a proc as an argument and return a new hash. If a 
proc 
# is not given, your method should provide default merging behavior. Do not 
use 
# Hash#merge in your method. 

    def my_merge(hash, &prc) 
    prc ||=Proc.new{|k,oldval,newval|} 
    newhash=Hash.new 

    self.each do |k,v| 
     newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v 
    end 

    hash.each do |k,v| 
     newhash[k]=v if newhash[k].nil? 
    end 
    newhash 
    end 
end 

任何幫助將不勝感激。謝謝!

回答

4

你誤解的路線是三元的,但操作的順序並不是你所期待的。當紅寶石看到:

newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v 

它認爲它作爲

newhash[k]= (hash[k] ? prc.call(k,v,hash[k]): v) 

所以,如果hash[k]是一個道理y的值,調用proc和結果分配給newhash[k];否則,將v指定爲newhash[k]

你可以看到,這是通過查看Operator Precedence文件,裏面有紅寶石如何看待這條線(很多其他的運營商之間):

,:

修改救援

=,+ =, - =等

所以三元比賦值操作符的優先級更高。


另外值得一提的是newhash[k] = hash[k]是,在Ruby中,真/假聲明,因爲賦值返回什麼分配:

a = 1 # => 1 
a = nil # => nil 

和一切除了false和紅寶石nil被視爲一個真y值,你可以做的東西,如:

if a = 2 
    puts "truthy" 
end 
# outputs 'truthy' 

if a = nil 
    puts "truthy" 
end 
# outputs nothing 

和適當的圓括號,你可以用它在三元:

(a = 1) ? 'truthy' : 'falsey' # => 'truthy' 
(a = nil) ? 'truthy' : 'falsey' # => 'falsey' 

雖然,這可能會造成混淆(通常是有條件的,你會看到===,而事實上我的紅寶石給我警告我在條件下使用=而不是==;但它可以完成

3

雖然當以這種方式使用三元組時,您的核心問題是operator precedence之一,但真正的問題是實際解決方案過於複雜。

如果你是剛剛開始使用Ruby盡最大努力避免類似的事情三元報表,讓您的代碼簡單明瞭越好。例如,什麼是以下DO:

a = nil 

a = true ? :yes : :no 

如果你認爲答案是「a分配:yes」那麼你正確地讀它。如果你覺得「a分配true」那麼你就忘記了三元?=用於分配的優先級高,因此首先發生。

,如果你看着它這樣同樣不會是真實的:

if (a = true) 
    :yes 
else 
    :no 
end 

還是另一種解釋清楚奠定:

a = 
    if (true) 
    :yes 
    else 
    :no 
    end 

如果這兩個結果是立即明顯甚至對編程有基本的瞭解。如果真的是深夜,並且您正在嘗試修復一個錯誤,並且您無法弄清自己的代碼,因爲這樣做太過複雜,這也可能非常方便。

話雖這麼說,一個經過改進的解決方案是這樣的:

class Hash 
    def my_merge(hash) 
    # Figure out all the keys that might show up in this merge in advance. 
    keys = (self.keys + hash.keys).uniq 
    newhash = { } 

    # Try all possible keys and evaluate what the result should be 
    keys.each do |k| 
     newhash[k] = 
     if (block_given?) 
      yield(k, self[k], hash[k]) 
     elsif (self.has_key?(k)) 
      self[k] 
     else 
      hash[k] 
     end 
    end 

    newhash 
    end 
end 

您可以利用if實際上返回在Ruby中值,使這個問題的短期工作,並保持內在邏輯非常清晰的方式。

需要注意的一點是,當你使用Hash.new你大概的意思,而不是{ }。正式聲明保留用於Hash.new(0)Hash.new { |h,k| h[k] = [ ] }之類的內容,其中第一個設置爲靜態默認設置,第二個設置爲計算的默認設置。如果您沒有設置默認值,請不要使用顯式初始值設定項。它只是增加了噪音。

您還可以更好地利用紅寶石的內置功能,使顯著減少你需要臨時變量的數量,再加上你真正需要做的,以實現轉換這樣的工作量。例如,一個小整理,你會得到這樣的:

class Hash 
    def my_merge(hash) 
    (self.keys + hash.keys).uniq.map do |k| 
     [ 
     k, 
     if (block_given?) 
      yield(k, self[k], hash[k]) 
     elsif (self.has_key?(k)) 
      self[k] 
     else 
      hash[k] 
     end 
     ] 
    end.to_h 
    end 
end 

這是相當精幹的代碼最大的一塊有你的merge implmentation,因爲它應該是,不是所有的設置和清理代碼。