2015-09-28 59 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 

现在的价值进行评估的第一次搜索键。

但是,请注意,直到您搜索它们(因为它们也会被延迟添加),键才会出现在哈希中。