簡體   English   中英

遞歸函數組合中的懶惰

[英]Laziness in recursive function composition

我試圖理解 Haskell 的惰性求值,並在GHCi進行了以下嘗試

GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Prelude> :set +m
Prelude> let iterateUntilError1 :: (a->a) -> (a->a)
Prelude|     iterateUntilError1 f = (iterateUntilError1 f) . f
Prelude| 
Prelude> let iterateUntilError2 :: (a->a) -> (a->a)
Prelude|     iterateUntilError2 f = \x -> (iterateUntilError2 f) . f $ x
Prelude| 
Prelude> let iterateUntilError3 :: (a->a) -> (a->a)
Prelude|     iterateUntilError3 f = \x -> (iterateUntilError3 f) . f $! x
Prelude| 
Prelude> let iterateUntilError4 x = foldl1 (.) (repeat (($!) x))
Prelude> iterateUntilError3 tail [1..5]
*** Exception: Prelude.tail: empty list
Prelude> iterate tail [1..5]
[[1,2,3,4,5],[2,3,4,5],[3,4,5],[4,5],[5],[],*** Exception: Prelude.tail: empty list

在提供的 4 個版本中,只有iterateUntilError3按預期工作,其他 3 個版本進入無限循環,必須通過Ctrl + C停止。 我不太明白為什么其他三個版本在這種情況下不起作用。

相關的問題懶惰和函數組合(haskell,erlang)似乎沒有解決這個問題中提出的問題。

讓我們從iterateUntilError1開始。 如果您嘗試評估

iterateUntilError1 tail [1..5] =

展開iterateUntilError1定義:

((iterateUntilError1 tail) . tail) [1..5] =

擴展定義.

(iterateUntilError1 tail) (tail [1..5]) =

等等,我們iterateUntilError1 tail了!

((iterateUntilError1 tail) . tail) (tail [1..5]) =
(iterateUntilError1 tail) (tail (tail [1..5]))

等等。

使用iterateUntilError3您有(插入一些額外的括號以使優先級更清晰)

iterateUntilError3 tail [1..5] =
((iterateUntilError3 tail) . tail) $! [1..5] =
((iterateUntilError3 tail) . tail) (1:[2..5]) = 
iterateUntilError3 tail (tail (1:[2..5])) =
(iterateUntilError3 tail . tail) $! (tail (1:[2..5])) =
(iterateUntilError3 tail . tail) (2:[3..5]) = ...

所以它最終會出錯。

使用iterateUntilError2 ,您有類似的評估,直到最后一行 where $! 強制tail減少直到獲得構造函數,而$不會:

(iterateUntilError2 tail . tail) $ (tail [1..5]) =
(iterateUntilError2 tail . tail) (tail [1..5]) =
(iterateUntilError2 tail) (tail (tail [1..5])) = ...

最后(為了簡單起見,使用tail_ for (tail $!) ):

iterateUntilError4 tail [1..5] =
foldl1 (.) (repeat tail_) [1..5] = 
foldl (.) tail_ (repeat tail_) [1..5] =
foldl (.) (tail_ . tail_) (repeat tail_) [1..5] =
foldl (.) (tail_ . tail_ . tail_) (repeat tail_) [1..5] = ...

(對於這個我沒有遵循實際的定義,但我認為這個想法應該仍然是正確的)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM