Ruby - Lazily评估哈希

时间:2015-09-28 10:40:33

标签: ruby hash promise lazy-evaluation

我试图在Ruby中创建一个延迟评估的哈希:

hash = {foo: -> {go_find_foo} }

我不希望调用者知道他们可能会获得proc而不是值 - 理想情况下我希望Hash类获取每个获取值是否为{{1}执行它,将哈希值设置为lambda的返回值,最后将值返回给客户端。

理想情况下,我希望能够使用标准语法定义这些哈希值,如上面的代码示例所示。这意味着我无法真正使用扩展,因为我需要手动调用构造函数(proc)。如果我进行猴子补丁,那么我就无法委托hash = LazyHash.new的原始实现。

有什么想法吗?有没有图书馆已经这样做了?在解释[]符号时,有没有办法告诉Ruby哪个Hash实现实例化?

4 个答案:

答案 0 :(得分: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

这有一个明显的缺点,即消除了在代码库中任何地方都有HashProc值的lambda地图的可能性,可能会破坏Rails 。考虑使用类似refinements的内容来缩小猴子补丁的范围。

答案 1 :(得分:1)

GFD - 不确定我错过了这个:http://promise.rubyforge.org/

答案 2 :(得分: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 :(得分:0)

您可以使用https://mozilla.github.io/nunjucks/api.html#customizing-syntax

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

现在,您将在第一次搜索密钥时评估这些值。

但请注意,在您搜索密钥之前,密钥不会出现在哈希中(因为它们也会延迟添加)。