在某些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
这仍然是一行,但非常重复。
是否有更好的方法可以轻松返回缓存结果,无论值是什么?
答案 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