Esiste un consenso su come evitare la memoizzazione causando bug dovuti allo stato mutabile?Come si dovrebbe evitare la memoizzazione causando bug in Ruby?
In questo esempio, un risultato memorizzato nella cache ha avuto lo stato modificato e pertanto ha dato il risultato errato la seconda volta che è stato chiamato.
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
Approcci che posso vedere per evitare questo sono:
greeting
potrebbe restituire undup
oclone
di@greeting_cache[formality]
greeting
potrebbefreeze
il risultato di@greeting_cache[formality]
. Ciò farebbe sorgere un'eccezione quandomemoization_mutator
aggiunge delle stringhe ad esso.- Controllare tutto il codice che utilizza il risultato di
greeting
per assicurarsi che non si verifichi alcun mutamento della stringa.
Esiste un consenso sull'approccio migliore? L'unico svantaggio di fare (1) o (2) diminuisce le prestazioni? (Sospetto anche il congelamento di un oggetto potrebbe non funzionare completamente se ha riferimenti ad altri oggetti)
Nota a margine: questo problema non influisce sull'applicazione principale della memoizzazione: come Fixnum
s sono immutabili, il calcolo delle sequenze di Fibonacci non lo fa avere problemi con lo stato mutabile. :)
Un piccolo commento sullo stile: è possibile semplificare il metodo di saluto con l'operatore || =. Così: def saluto (formalità); @greeting_cache [formality] || = cost_greeting_calculation (formality); fine – zaius
@zaius: Questo funziona nella maggior parte degli scenari, ma non funzionerebbe se 'nil' o' false' fossero un valore valido. –
Ah, vero. Colpa mia. – zaius