假设我们在 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' 一样简单?还是二进制折叠会增加额外的问题?