2015-09-28 60 views
1

我想在Ruby中創建一個惰性計算哈希值:紅寶石 - 懶洋洋地評估的哈希

hash = {foo: -> {go_find_foo} } 

我不希望來電者知道,他們可能會得到一個proc回,而不是一個值 - 理想情況下,我希望Hash類能夠分別獲取值是否爲proc,執行它,將Hash中的值設置爲lambda的返回值,最後將值返回給客戶端。

理想情況下,我希望能夠使用標準語法定義這些散列,如上面的代碼示例中所示。這意味着我不能真正使用擴展,因爲我需要手動調用構造函數(hash = LazyHash.new)。如果我猴子補丁,那麼我不能委託給原來的執行[]

任何想法?是否有任何圖書館已經這樣做?有什麼方法可以告訴Ruby在解釋{}符號時實例化Hash的哪個實現?

回答

0

Hash構造函數可以接受一個塊,計算響應。你可以使用你的問題:

hash = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } 

所以,你可以創建一個新的類,它繼承了Hash,你可以做到以下幾點:

class CalculatedHash < Hash 
    def initialize(rules) 
    super() do |hash, key| 
     hash[key] = rules.fetch(key).call 
    end 
    end 
end 

然後:

hash = CalculatedHash.new(foo: -> { go_find_foo }) 
3

你可以使用alias_method存儲[]的原始實現,然後在重寫的方法中引用它。

這是一個使用猴子修補的工作實現。

class Hash 
    alias_method :old_brackets, :[] 

    def [](member) 
    value = old_brackets(member) # Delegate to old implementation 
    self[member] = value.call if value.respond_to?(:call) 
    value 
    end 
end 

這具有消除有Hash地圖Proclambda值隨時隨地在你的代碼庫,可能打破的Rails的可能性的明顯缺點。考慮使用諸如refinements之類的東西來縮小猴子補丁的範圍。

+1

不是一個好主意,做一個哈希內,因爲這肯定會打破一些鋼軌方法。可能更安全的方法是創建Hash的子類(然後,您可以不使用別名,而是調用super來代替)。 – BroiSatse

+0

@BroiSatse:你絕對正確。海報明確表示,他想保留哈希文字,但。我不是說這是個好主意。我通常不喜歡猴子補丁。不確定是否可以使用Rails進行這種遊戲,也許使用改進,但這基本上是他要求的。我將添加一個與Rails無關的潛在問題。謝謝! – Drenmi

+0

我非常喜歡猴子修補,但只用於擴展給定的類,而不是修改它。在這種情況下,我們可以添加一個'dynamic'或者某個方法來返回我們的動態版本的哈希? – BroiSatse

0

可以使用hash block constructor

def go_find(key) 
    "shiny #{key} value" 
end 

h = Hash.new do |hash, key| 
    hash[key] = go_find(key) if [:foo, :bar, :baz].include? key 
end 

h[:foo] # => "shiny foo value" 
h[:quz] # => nil 

現在的價值進行評估的第一次搜索鍵。

但是,請注意,直到您搜索它們(因爲它們也會被延遲添加),鍵纔會出現在哈希中。