2011-01-13 55 views
8

關於如何避免由於可變狀態引起的錯誤導致錯誤,是否存在共識?我應該如何避免在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. greeting可以返回dupclone@greeting_cache[formality]
  2. greeting可以freeze@greeting_cache[formality]結果。當memoization_mutator將字符串附加到它時,會引發異常。
  3. 檢查所有使用greeting的結果的代碼,以確保它沒有任何字符串變異。

對最佳方法有共識嗎?做(1)或(2)性能下降的唯一缺點是? (我也懷疑凍結一個對象可能無法完全工作,如果它有其他對象的引用)

注意:這個問題不影響memoization的主要應用:因爲Fixnum是不可變的,計算斐波那契數列不有可變狀態的問題。 :)

+0

對樣式的小評論 - 您可以使用|| =運算符來簡化問候方法。像這樣:def問候(形式); @greeting_cache [形式] || = expensive_greeting_calculation(形式);結束 – zaius

+0

@zaius:在大多數場景下都可以工作,但如果'nil'或'false'是一個有效的值,則不起作用。 –

+0

啊,真的。我的錯。 – zaius

回答

4

我會傾向於返回一個克隆的對象。創建新字符串的性能命中幾乎沒有。並凍結暴露實施細節。

0

我還是'紅寶石新手',我不知道你是否知道'< <'和'+'方法之間的區別。

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 
+0

感謝您的建議,但我更多的是更通用的解決方案之後。 –

相關問題