【问题标题】:Ruby - Lazily Evaluated HashRuby - 惰性求值哈希
【发布时间】:2015-12-25 13:53:50
【问题描述】:

我正在尝试在 Ruby 中创建一个惰性求值的哈希:

hash = {foo: -> {go_find_foo} }

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

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

有什么想法吗?是否有任何图书馆已经这样做了?在解释 {} 符号时,有什么方法可以告诉 Ruby 实例化 Hash 的哪个实现?

【问题讨论】:

    标签: ruby hash promise lazy-evaluation


    【解决方案1】:

    GFD - 不知道我是怎么错过的:http://promise.rubyforge.org/

    【讨论】:

      【解决方案2】:

      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
        

        这有一个明显的缺点,即消除了在代码库中的任何位置使用带有 Proclambda 值的 Hash 映射的可能性,可能会破坏 Rails。考虑使用 refinements 之类的东西来缩小猴子补丁的范围。

        【讨论】:

        • 在 Hash 中这样做不是一个好主意,因为这肯定会破坏一些 rails 方法。可能更安全的方法是创建 Hash 的子类(然后您可以不使用别名并调用 super 来代替)。
        • @BroiSatse:您绝对正确。不过,张贴者明确表示他想保留哈希文字。我并不是说这是一个好主意。我通常反对猴子补丁。不确定是否可以使用 Rails 进行此操作,也许使用改进,但这基本上是他所要求的。我将添加一个关于 Rails 潜在问题的不。谢谢!
        • 我非常喜欢猴子补丁,但仅用于扩展给定的类,而不是修改它。在这种情况下,我们可以添加一个方法 dynamic 或 sth 来返回我们动态版本的哈希?
        【解决方案4】:

        您可以使用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
        

        现在将在您第一次搜索键时评估值。

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

        【讨论】:

          猜你喜欢
          • 2019-04-24
          • 1970-01-01
          • 2012-11-06
          • 1970-01-01
          • 2013-10-01
          • 2016-03-07
          • 1970-01-01
          • 2018-04-04
          • 2014-04-09
          相关资源
          最近更新 更多