跳轉到內容

Haskell/嚴格性

來自華夏公益教科書,開放書籍,開放世界

嚴格評估和惰性評估的區別

[編輯 | 編輯原始碼]

嚴格評估,或稱為急切評估,是一種評估策略,其中表達式在繫結到變數後立即進行評估。例如,在嚴格評估中,當讀取 x = 3 * 7 時,3 * 7 會立即計算,並將 21 繫結到 x。相反,在惰性評估 中,值只有在需要時才會計算。在 x = 3 * 7 的示例中,3 * 7 只有在需要時才會評估,比如你想要輸出 x 的值。

為什麼惰性評估可能會造成問題

[編輯 | 編輯原始碼]

惰性評估通常涉及稱為 thunk 的物件。thunk 是一個佔位符物件,它指定了不是資料本身,而是如何計算該資料。一個實體可以用 thunk 代替,以計算該實體。當一個實體被複制時,它是否是一個 thunk 並不重要 - 它會被按原樣複製(在大多數實現中,會建立一個指向資料的指標)。當一個實體被評估時,首先會檢查它是否是一個 thunk;如果是 thunk,則執行它,否則返回實際資料。正是 thunk 的魔力使得惰性評估能夠實現。

一般來說,在實現中,thunk 實際上只是一個指向程式碼片段(通常是靜態程式碼)的指標,加上另一個指向程式碼應該處理的資料的指標。如果 thunk 計算的實體大於指向程式碼和相關資料的指標,那麼 thunk 在記憶體使用方面就佔優勢。但是,如果 thunk 計算的實體更小,那麼 thunk 最終會使用更多記憶體。

例如,考慮使用表示式 iterate (+ 1) 0 生成的無限長度列表。列表的大小是無限的,但程式碼只是一個加法指令,兩個資料 1 和 0 只是兩個整數。在這種情況下,表示該列表的 thunk 比實際列表佔用更少的記憶體,而實際列表會佔用無限的記憶體。

然而,再舉個例子,考慮使用表示式 4 * 13 + 2 生成的數字。該數字的值是 54,但在 thunk 形式下,它是一個乘法、一個加法和三個數字。在這種情況下,thunk 在記憶體方面處於劣勢。

通常,上面第二種情況會消耗如此多的記憶體,以至於會消耗整個堆並強制垃圾收集器執行。這會顯著降低程式的執行速度。事實上,這就是惰性評估可能造成問題的最主要原因。

此外,如果最終結果值確實被使用了,那麼就不會節省任何計算量;相反,會為構建 thunk 付出一些輕微的開銷(常數因子)。然而,這並不是程式設計師在大多數情況下應該處理的開銷;應該考慮更重要的因素,這些因素可能會帶來更大的改進;此外,像 GHC 這樣的最佳化 Haskell 編譯器可以執行“嚴格性分析”並消除這種輕微的開銷。

嚴格性註解

[編輯 | 編輯原始碼]

參考文獻

[編輯 | 編輯原始碼]
華夏公益教科書