缓存,当为零或假是可接受的结果

时间:2014-02-10 20:30:47

标签: ruby-on-rails ruby activerecord

在某些ruby类中,使用||=运算符缓存昂贵操作的结果非常有用,如下面的代码段所示:

class CacheableCalculations
  def foobar
    @foobar ||= lambda { some_expensive_calculation }.call
  end
end

如果返回的值为nil或false,则会出现问题,如此测试所示:

class Example
  attr_accessor :counter

  def initialize(value)
    @counter = 0
    @value = value
  end

  def fancy_calculation
    @foo ||= lambda { @counter += 1; @value }.call
  end
end

first = Example.new(true)
5.times { first.fancy_calculation }
puts first.counter  # <== 1, expected

second = Example.new(false)
5.times { second.fancy_calculation }
puts second.counter  # <== 5, not caching

third = Example.new(nil)
5.times { third.fancy_calculation }
puts third.counter  # <== 5, not caching

使用defined?运算符是否有任何优缺点,如下面的代码块所示?

class Example
  attr_accessor :counter

  def initialize(value)
    @counter = 0
    @value = value
  end

  def fancy_calculation
    (defined? @foo) ? @foo : (@foo = lambda { @counter += 1; @value }.call)
  end
end

这仍然是一行,但非常重复。

是否有更好的方法可以轻松返回缓存结果,无论值是什么?

2 个答案:

答案 0 :(得分:0)

编写方式的问题是三元运算符?:的优先级高于赋值=所以它被解析为

def fancy_calculation
  ((defined? @foo) ? @foo : @foo) = lambda { @counter += 1; @value }.call # NO
end
想要的

,因为始终会将@foo分配给。

相反,这样做

def fancy_calculation
  defined?(@foo) ? @foo : (@foo = lambda { @counter += 1; @value }.call)
end

如果不使用专门用于记忆的单独包装/功能,这可能是非常简洁的。

答案 1 :(得分:0)

您要实现的目标称为memoization。曾经有一种方法在Rails中做你需要的东西,但在某些时候它们被提取到一个单独的memoist宝石。看看:https://github.com/matthewrudy/memoist

还有另一种选择:https://github.com/dkubb/memoizable