我定义了一个二叉树,并使用我的函数创建了一个新树。
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
singleton :: a -> Tree a
singleton x = Node x EmptyTree EmptyTree
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
| otherwise = Node a left right
当我在终端中执行此操作时:
let nums = [8,6,4,1,7,3,5]
let numsTree= foldl treeInsert EmptyTree nums
返回错误。
Occurs check: cannot construct the infinite type: a ~ Tree a
Expected type: Tree a -> Tree a -> Tree a
Actual type: a -> Tree a -> Tree a
但是,在我将foldl更改为foldr之后,它就可以工作了。
let numsTree= foldr treeInsert EmptyTree nums
谁能告诉我为什么?在这种情况下,foldl和foldr有什么区别,我不清楚。谢谢!
答案 0 :(得分:2)
foldr
和foldl
之间有两个区别。重要的是它们括起来计算的方式:
foldl (+) 0 [1..3] = ((0 + 1) + 2) + 3
foldr (+) 0 [1..3] = 1 + (2 + (3 + 0))
另一个是他们希望他们的第一个参数(函数)以相反的顺序接受 个参数:
> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
我相信造成第二个差异的唯一原因是要匹配表达式在第一个代码段中的写入顺序。您应根据所需的括号顺序在函数之间进行选择,并在必要时翻转函数以使其匹配。
因此,在您的情况下,这两个选项是:
foldr treeInsert EmptyTree nums
foldl (flip treeInsert) EmptyTree nums
答案 1 :(得分:1)
foldl
的签名是:
foldl :: (b -> a -> b) -> b -> [a] -> b
我们将foldl
与函数(insertTree
)以及Tree
和Int
的列表一起应用,这意味着b ~ Tree Int
,和a ~ Int
。
所以这意味着到目前为止构造的Tree
应该是这里的第一个参数。
我们可以使用flip :: (a -> b -> c) -> b -> a -> c
解决此问题:
let numsTree = foldl (flip treeInsert) EmptyTree nums
flip
翻转参数,所以f x y == flip f y x
。
foldl
意味着对于列表[8,6,4]
,我们将其应用为:
insertTree 4 (insertTree 6 (insertTree 8 EmptyTree))
我们还可以决定使用foldr :: (b -> a -> b) -> b -> [a] -> b
(注意参数的顺序已更改),例如:
let numsTree = foldr treeInsert EmptyTree nums
然后输入列表[8,6,4]
,结果将是:
insertTree 8 (insertTree 6 (insertTree 4 EmptyTree))
由于元素在Tree
中插入的顺序可能会产生影响,因此两者在上 ,不是等效的。