比方說,我們有一個簡單的樹在Haskell創建算法:嚴格指導遞歸?
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
makeTree :: Tree Int -> Tree Int
makeTree (Node 0 l r) = Node 0 EmptyTree EmptyTree
makeTree (Node n l r) = Node n (makeTree $ newTree (n - 1))
(makeTree $ newTree (n - 1))
where
newTree n = Node n EmptyTree EmptyTree
對於非常大的數字,我們希望這個算法失敗,一個「堆棧大小溢出」錯誤。這是因爲該算法是二進制遞歸,而不是尾遞歸。我可以使用bang模式(在產生的左子樹「!(makeTree $ newTree(n-1))」)引導二進制遞歸進入尾遞歸,因爲遞歸現在應該由於嚴格性而定向?
編輯:
事實證明,真正的問題是沒有樹的創作,但功能消耗的樹。有用來壓平樹的另一個功能,其中實例如下:因此
import qualified Data.Foldable as F
instance F.Foldable Tree where
foldMap f EmptyTree = mempty
foldMap f (Node x l r) = F.foldMap f l `mappend`
f x `mappend`
F.foldMap f r
flatten = F.foldMap (:[])
調用樹的扁平化,這是可能在這些地方發生溢出。如果是這樣,解決方案就像假設將foldl轉換爲foldl'一樣簡單?還是二元摺疊會增加額外的問題?
咦?即使這是嚴格的,這些遞歸也不能是尾部的,因爲尾部調用必須是函數的最後一件事。在這裏你也調用了這兩個遞歸之後調用Node構造函數。 – tohava
這段代碼是不可編譯的 –
如果你修復了編譯的代碼,這個函數就不會發生堆棧溢出。懶惰有幫助... – augustss