懶惰評估使得它很難推論,當事情進行評估和懶惰沒有副作用以及工作
惰性列表的內存消耗(一般懶惰的數據結構)是很難對付因爲它們會導致記憶中未被評估的thunk的積累。
懶惰不適合副作用。在副作用的情況下,副作用何時發生在計算問題上。由於評估推遲,副作用的時間很難推理。例如,當全局變量被讀取/寫入時,它很重要。當db寫/讀發生並且打印到控制檯時,它很重要
Thunk
表示延遲計算被評估或未評估的表達式。
懶惰流(或懶惰列表)導致佔用內存的thunk(計算描述)。除非明確要求,否則這些thunk不會被評估。在程序執行過程中,內存消耗可能會非常高,因爲未被評估的thunk在內存中累積。這也是haskell列表的經典問題。默認情況下,haskell是懶惰的。這就是爲什麼有一個嚴格的摺疊版本和哈希克爾摺疊的懶惰版本來處理這類問題。
讓檢查與Stream
代碼
這裏是過濾器,從標準庫咚tl
/** A lazy cons cell, from which streams are built. */
@SerialVersionUID(-602202424901551803L)
final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A] {
override def isEmpty = false
override def head = hd
@volatile private[this] var tlVal: Stream[A] = _
@volatile private[this] var tlGen = tl _
def tailDefined: Boolean = tlGen eq null
override def tail: Stream[A] = {
if (!tailDefined)
synchronized {
if (!tailDefined) {
tlVal = tlGen()
tlGen = null
}
}
tlVal
}
}
tl
是代碼
override def filter(p: A => Boolean): Stream[A] = {
// optimization: drop leading prefix of elems for which f returns false
// var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
var rest = this
while (!rest.isEmpty && !p(rest.head)) rest = rest.tail
// private utility func to avoid `this` on stack (would be needed for the lazy arg)
if (rest.nonEmpty) Stream.filteredTail(rest, p)
else Stream.Empty
}
private[immutable] def filteredTail[A](stream: Stream[A], p: A => Boolean) = {
cons(stream.head, stream.tail filter p)
}
這裏是懶洋洋地評估發生了什麼大塊在內存中一直存在ing會觸發它的評估。 Thunk可能還會產生多個其他的thunk,這些thunk可能會保持未評估的狀態並佔用內存。
Stream(1, 2, 3, 4, 5, 6).filter(_ % 2 == 0)
cons(2, thunk)
cons(2, cons(4, thunk)
cons(2, 4, 6, thunk)
有什麼規則或建議什麼時候更喜歡一個或另一個?是否總是需要測量它們? – Randomize
是的,有很多地方「懶惰」比嚴格的評估表現更好。但它很好地測試大輸入的懶惰程序來檢查它們的內存腳印 – pamu
@pamu:感謝您的詳細解釋。這是有幫助的。 – Samar