Haskell:foldr vs foldr1

时间:2012-12-08 21:37:19

标签: haskell fold

如果我有这个插入功能:

insert x []     = [x]
insert x (h:t)
  | x <= h      = x:(h:t)
  | otherwise   = h:(insert x t)

这会产生一个排序列表:

foldr insert [] [1,19,-2,7,43]

但是这个:

foldr1 insert [1,19,-2,7,43]

生成'无法构造无限类型:a0 = [a0] '

我很困惑为什么第二个电话不起作用。

我已经查看了foldrfoldr1的定义,并且已经使用简单的算术函数进行了跟踪,但我仍然无法清楚地解释为什么第二次调用失败。

5 个答案:

答案 0 :(得分:16)

让我们看看一些类型的签名。

foldr  :: (a -> b -> b) -> b -> [a] -> b
foldr1 :: (a -> a -> a) ->      [a] -> a

在这两种情况下,第一个参数都是两个参数的函数。

  • 对于foldr1,这两个参数必须具有相同的类型(结果也具有此类型)
  • 对于foldr,这两个参数可能有不同的类型(结果与第二个参数的类型相同)

insert的类型是什么?

答案 1 :(得分:9)

我喜欢这里给出的基于类型的答案,但我也想提供一个可操作的答案,解释为什么我们不希望这样做。让我们从foldr1的来源开始:

foldr1          :: (a -> a -> a) -> [a] -> a
foldr1 _ [x]    = x
foldr1 f (x:xs) = f x (foldr1 f xs)

现在,让我们尝试运行您的程序。

foldr1 insert [1,19,-2,7,43]
= { list syntax }
foldr1 insert (1:[19,-2,7,43])
= { definition of foldr1 }
insert 1 (foldr1 insert [19,-2,7,43])
= { three copies of the above two steps }
insert 1 (insert 19 (insert (-2) (insert 7 (foldr1 insert [43]))))
= { definition of foldr1 }
insert 1 (insert 19 (insert (-2) (insert 7 43)))

...哎呦!那insert 7 43永远不会起作用。 =)

答案 2 :(得分:3)

foldr1的类型是(a -> a -> a) -> [a] -> a,即此函数的参数和结果具有相同的类型。但是,insert的类型为Ord a => a -> [a] -> [a]。对于foldr1 insert输入良好的类型,a[a]必须是同一类型。

但这意味着a == [a] == [[a]] == [[[a]]] == ...,即a是一种无限多列表。

答案 3 :(得分:1)

因为foldr1正在尝试这样做:

insert 43 -7

会失败。

答案 4 :(得分:1)

问题在于:

foldr会这样做:

  1. 结果设置为insert 43 []
  2. 结果设置为insert 7 result
  3. 这显然有效。

    foldr1会尝试执行以下操作:

    1. 结果设置为insert 7 43
    2. 结果设置为insert -2 result
    3. 这绝对是错误的。您可以看到,foldr1要求操作的参数属于同一类型,而insert则不是这样。