【问题标题】:Ruby Pascal's triangle generator with memoizationRuby Pascal 的带记忆功能的三角形生成器
【发布时间】:2012-11-06 05:22:58
【问题描述】:

我正在尝试记忆我的 Pascal 三角形生成器的实现,作为 Ruby 学习实验。我有以下工作代码:

module PascalMemo
  @cache = {}
  def PascalMemo::get(r,c)
    if @cache[[r,c]].nil? then 
      if c == 0 || c == r then 
        @cache[[r,c]] = 1
      else
        @cache[[r,c]] = PascalMemo::get(r - 1, c) + PascalMemo::get(r - 1, c - 1)
      end
    end
    @cache[[r,c]]
  end
end

def pascal_memo (r,c)
  PascalMemo::get(r,c)
end

这可以更简洁吗?具体来说,我可以比这更简单地创建一个具有局部闭包的全局范围函数吗?

【问题讨论】:

    标签: ruby closures memoization


    【解决方案1】:
    def pascal_memo
      cache = [[1]]
      get = lambda { |r, c|
        ( cache[r] or cache[r] = [1] + [nil] * (r - 1) + [1] )[c] or
          cache[r][c] = get.(r - 1, c) + get.(r - 1, c - 1)
      }
    end
    
    p = pascal_memo
    p.( 10, 7 ) #=> 120
    

    请注意,上面的构造确实实现了记忆化,它不仅仅是一个简单的递归方法。

    【讨论】:

    • 我从中学到了很多。我不想留下pascal_memo 工厂函数,但我能够通过使工厂函数本身成为lambda 并将p 分配给内联调用它的结果来实现这一点。 .(r,c) 语法糖对我来说也是新的。最后,函数本身在构建缓存的方式上很聪明——尽管它牺牲了相当多的可读性:(
    • 我很高兴它有帮助。至于缓存构建,我只是想提出一个替代方案。请注意,缓存本身是在存储在 lambda 绑定中的局部变量中构建的,而不是在 main 中。
    【解决方案2】:

    这可以更简洁吗?

    IMO 似乎很清楚,moduleing 通常是一种很好的直觉。

    我可以比这更简单地创建一个具有局部闭包的全局范围函数吗?

    另一种选择是递归lambda

    memo = {}
    
    pascal_memo = lambda do |r, c|
      if memo[[r,c]].nil?
        if c == 0 || c == r
          memo[[r,c]] = 1
        else
          memo[[r,c]] = pascal_memo[r - 1, c] + pascal_memo[r - 1, c - 1]
        end
      end
      memo[[r,c]]
    end
    
    pascal_memo[10, 2]
    # => 45
    

    【讨论】:

    • 如果我可以问,@cache 实例变量属于什么对象?
    • @BorisStitnicky:很好,这是复制粘贴的保留。根本不需要实例变量
    • 这很有用,因为我不知道如何创建递归 lambda。但是,我接受了另一个答案,因为它没有将缓存留在全局范围内。
    【解决方案3】:

    我找到了一种方法来完成我想要的稍微令人满意的事情,因为它产生的是函数而不是 lambda:

    class << self
      cache = {}
    
      define_method :pascal_memo do |r,c|
        cache[[r,c]] or 
        (if c == 0 or c == r then cache[[r,c]] = 1 else nil end) or 
        cache[[r,c]] = pascal_memo(r-1,c) + pascal_memo(r-1,c-1)
      end
    end
    

    这将为主要对象打开元类/单例类,然后使用 define_method 添加一个新方法来关闭缓存变量,然后该缓存变量超出了除 pascal_memo 方法之外的所有范围。

    【讨论】:

      猜你喜欢
      • 2017-08-11
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      • 2013-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多