Как мне избежать того, чтобы мемоизация вызывала ошибки в Ruby?

Есть ли консенсус о том, как избежать запоминания, вызывающего ошибки из-за изменяемого состояния?

В этом Например, состояние кэшированного результата изменилось, и поэтому при втором вызове он дал неправильный результат.

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

Я вижу подходы, позволяющие избежать этого:

  1. приветствие может вернуть dup или клон из @greeting_cache [формальность]
  2. приветствие может заморозить результат @greeting_cache [формальность] . Который' d вызывает исключение, когда memoization_mutator добавляет к нему строки.
  3. Проверьте весь код, который использует результат приветствия , чтобы убедиться, что ни один из них не изменяет строку.

Есть ли консенсус в отношении наилучшего подхода? Единственным недостатком является снижение производительности (1) или (2)? (Я также подозреваю, что замораживание объекта может не работать полностью, если он имеет ссылки на другие объекты)

Примечание: эта проблема не влияет на основное приложение мемоизации: поскольку Fixnum являются неизменяемыми, вычисление Последовательности Фибоначчи не имеют проблем с изменяемым состоянием. :)

8
задан Andrew Grimm 13 January 2011 в 01:37
поделиться