使用foldl时无法构造无限类型

时间:2018-09-22 13:06:23

标签: haskell haskell-stack

我定义了一个二叉树,并使用我的函数创建了一个新树。

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有什么区别,我不清楚。谢谢!

2 个答案:

答案 0 :(得分:2)

foldrfoldl之间有两个区别。重要的是它们括起来计算的方式:

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)以及TreeInt的列表一起应用,这意味着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中插入的顺序可能会产生影响,因此两者在是等效的。