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