Как следует избегать запоминания, вызывающего ошибки в Ruby? - PullRequest
8 голосов
/ 13 января 2011

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

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

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. greeting может вернуть dup или clone из @greeting_cache[formality]
  2. greeting может freeze результат @greeting_cache[formality].Это приведет к возникновению исключения, когда memoization_mutator добавляет к нему строки.
  3. Проверьте весь код, который использует результат greeting, чтобы убедиться, что ни один из них не изменяет строку.

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

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

Ответы [ 2 ]

4 голосов
/ 13 января 2011

Я бы склонялся к возвращению клонированного объекта.Хит производительности при создании новой строки почти ничего не значит.А замораживание раскрывает детали реализации.

0 голосов
/ 09 октября 2014

Я по-прежнему "рубиновый новичок", и я не знаю, знали ли вы о разнице между методами "<<" и "+" для строки. </p>

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
...