通過 Representables進行記憶的文章很好地解釋了如何通過 Representables 記憶遞回函式。它首先將斐波那契數列實作為 的不動點fibOp:
fibOp :: Num a => (Natural -> a) -> (Natural -> a)
fibOp v 0 = 0
fibOp v 1 = 1
fibOp v n = v (n-1) v (n-2)
fix f = let x = f x in x
fibNaive :: Num a => Natural -> a
fibNaive = fix fibOp
此實作效率不高,因為它多次計算相同的值。
文章繼續介紹互反函式streamTabulate和streamIndex(稍后將在Representable型別類中推廣)。這些函式允許我們實作一個記憶化的版本fibNaive:
fibSmart :: Num a => Natural -> a
fibSmart = fix (streamIndex . streamTabulate . fibOp)
正是在這一點上,文章變得有些“手搖”:
如果我們用 fibOp 組合我們的制表函式,我們會得到一個函式將函式 v 轉換為 Stream,我們可以通過索引來獲取函式。但是,在這種情況下,所有引數共享相同的流。
這究竟是什么意思?顯然,這種記憶技術之所以有效,是因為 Haskell 的惰性求值策略,并且因為它盡量不“不必要地”求值運算式。例如,這個答案提到 Haskell 每次輸入其周圍的 lambda 運算式時最多計算一次任何給定的運算式。然而,周圍的 lambda 運算式streamTabulate每次由周圍誘導的迭代輸入一次fix,這意味著streamTabulate可能被多次評估。
這種方法為什么以及如何起作用?它利用了 Haskell 評估策略的哪個方面?
uj5u.com熱心網友回復:
你的功能:
fibSmart :: Num a => Natural -> a
fibSmart = fix (streamIndex . streamTabulate . fibOp)
確實可以通過行內擴展(.)為:
fibSmart = fix (\f -> streamIndex (streamTabulate (fibOp f)))
但是這里的 lambda\f -> ...只由fix函式輸入一次。
您還可以通過進一步行內看到fix:
fibSmart = let f = streamIndex (streamTabulate (fibOp f)) in f
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/517166.html
標籤:哈斯克尔记忆可代表的
