简体   繁体   English

我应该如何避免备忘录导致Ruby中的错误?

[英]How should I avoid memoization causing bugs in Ruby?

Is there a consensus on how to avoid memoization causing bugs due to mutable state? 关于如何避免由于易变状态而导致错误记录的记忆,是否存在共识?

In this example, a cached result had its state mutated, and therefore gave the wrong result the second time it was called. 在此示例中,缓存的结果的状态发生了变化,因此第二次调用时给出了错误的结果。

class Greeter

  def initialize
    @greeting_cache = {}
  end

  def expensive_greeting_calculation(formality)
    case formality
      when :casual then "Hi"
      when :formal then "Hello"
    end
  end

  def greeting(formality)
    unless @greeting_cache.has_key?(formality)
      @greeting_cache[formality] = expensive_greeting_calculation(formality)
    end
    @greeting_cache[formality]
  end

end

def memoization_mutator
  greeter = Greeter.new
  first_person = "Bob"
  # Mildly contrived in this case,
  # but you could encounter this in more complex scenarios
  puts(greeter.greeting(:casual) << " " << first_person) # => Hi Bob
  second_person = "Sue"
  puts(greeter.greeting(:casual) << " " << second_person) # => Hi Bob Sue
end

memoization_mutator

Approaches I can see to avoid this are: 我可以看到避免这种情况的方法是:

  1. greeting could return a dup or clone of @greeting_cache[formality] greeting可能会返回dup@greeting_cache[formality] clone
  2. greeting could freeze the result of @greeting_cache[formality] . greeting可能会freeze @greeting_cache[formality]的结果。 That'd cause an exception to be raised when memoization_mutator appends strings to it. memoization_mutator向其附加字符串时,将引发异常。
  3. Check all code that uses the result of greeting to ensure none of it does any mutating of the string. 检查所有使用greeting结果的代码,以确保它们均不对字符串进行任何突变。

Is there a consensus on the best approach? 最佳方法是否达成共识? Is the only disadvantage of doing (1) or (2) decreased performance? 做(1)或(2)降低性能的唯一缺点是吗? (I also suspect freezing an object may not work fully if it has references to other objects) (我也怀疑冻结某个引用了其他对象的对象可能无法完全发挥作用)

Side note: this problem doesn't affect the main application of memoization: as Fixnum s are immutable, calculating Fibonacci sequences doesn't have problems with mutable state. 旁注:此问题不会影响记忆的主要应用:由于Fixnum是不可变的,因此计算斐波那契序列不会导致可变状态问题。 :) :)

I would lean towards returning a cloned object. 我倾向于返回一个克隆的对象。 The performance hit of creating a new string is next to nothing. 创建新字符串对性能的影响几乎没有。 And freezing exposes implementation details. 冻结公开了实现细节。

I am still 'ruby newbie', and I don't know if you were aware about the difference between '<<' and '+' methods to a String. 我仍然是'ruby newbie',我不知道您是否知道String的'<<'和'+'方法之间的区别。

first_person = "Bob"
puts(greeter.greeting(:casual) + " " + first_person) # => Hi Bob
second_person = "Sue"
puts(greeter.greeting(:casual) + " " + second_person) # => Hi Sue

# str << obj → str
# str + other_str → new_str

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM