Есть ли консенсус в отношении того, как избежать запоминания, вызывающего ошибки из-за изменяемого состояния?
В этом примере состояние кэшированного результата было изменено, и поэтому во второй раз он вызывал неправильный результат.
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
Подходы, которые я вижу, чтобы избежать этого:
greeting
может вернуть dup
или clone
из @greeting_cache[formality]
greeting
может freeze
результат @greeting_cache[formality]
.Это приведет к возникновению исключения, когда memoization_mutator
добавляет к нему строки. - Проверьте весь код, который использует результат
greeting
, чтобы убедиться, что ни один из них не изменяет строку.
Есть ли консенсус по поводу лучшего подхода?Является ли единственным недостатком (1) или (2) снижение производительности?(Я также подозреваю, что замораживание объекта может работать не полностью, если на него есть ссылки на другие объекты) *
Примечание: эта проблема не влияет на основное применение запоминания: поскольку Fixnum
являются неизменяемыми, вычисляя Фибоначчипоследовательности не имеют проблем с изменяемым состоянием.:)