如何在Ruby中缓存方法结果

时间:2016-10-14 16:28:49

标签: ruby lazy-initialization

在Python中,我可以轻松地修饰方法,以便记住它们的结果:

def store(self):
    a = line1()
    b = line2(a)
    return line3(b)

=>

from lazy import lazy

@lazy
def store(self):
    a = line1()
    b = line2(a)
    return line3(b)

Ruby中是否有一些类似的习惯用法只计算一次方法结果?

3 个答案:

答案 0 :(得分:3)

在Ruby中,这通常称为memoization,它采用天真的形式:

def store
  @store ||= begin
    a = line1
    b = line2(a)
    line3(b)
  end
end

如果在多线程环境中使用此代码,则存在一些重要问题,这就是为什么有宝石可以管理它并确保您的惰性初始化程序只运行一次,如果这是一个问题。

答案 1 :(得分:2)

另一个适用于falsenil的选项:

def store
  unless defined?(@store) 
    a = line1
    b = line2(a)
    @store = line3(b)
  end

  @store
end

答案 2 :(得分:2)

不是真的。有一个运算符(||=)仅在左操作数为假(nilfalse)时才分配值。通常使用它和实例变量就可以了。

MemoistMemoizable是经常用于此目的的宝石。

但我的猜测是你的问题更普遍 - 你如何在ruby中实现方法装饰器?

class Module
  def decorate(method_name, &decoration)
    undecorated_method = instance_method(method_name)

    define_method(method_name) do
      decoration.call(&undecorated_method.bind(self))
    end
  end

  def memoize(method_name)
    @values ||= {}

    decorate(method_name) do |&evaluator|
      unless @values.key?(method_name)
        @values[method_name] = evaluator.call
      end

      @values[method_name]
    end
  end
end

class SuperComputer
  memoize def answer
    puts "Pending... 7½ million years remaining."
    42
  end
end

deep_mind = SuperComputer.new
deep_mind.answer # => 42 (and prints)
deep_mind.answer # => 42 (doesn't print)

现在,从这个实现(args,kwargs,块,每个实例的memoization等)中可以得到很多东西,但是它应该让你大致了解如何在不需要更改语言的情况下实现装饰器本身。