另一种类型错误

时间:2012-02-17 23:49:21

标签: haskell

为什么不进行编译?

append :: [a] -> [a] -> [a]
append xs ys = foldr (:) ys xs

traverse :: a -> [[a]] -> [[a]]
traverse x [[]] = [[x]]
traverse x [(y:ys)] = append [(x:y:ys)] (map (y:) (traverse x [ys]))

comb :: [a] -> [[a]]
comb [] = [[]]
comb (x:[]) = [[x]]
comb (x:y:[]) = [[x,y],[y,x]] 
comb (x:xs) = map (traverse x) (comb xs)

失败并出现此错误:

 Couldn't match type `a' with `[a]'
  `a' is a rigid type variable bound by
      the type signature for comb :: [a] -> [[a]] at pr27.hs:10:1
Expected type: [[a]]
  Actual type: [a]
In the first argument of `comb', namely `xs'
In the second argument of `map', namely `(comb xs)'

xs是列表的尾部,因此它是梳子的有效参数? 谢谢David Kramf

3 个答案:

答案 0 :(得分:9)

您的方法comb xs会返回[[a]]类型。传入map (traverse x)时,会调用traverse x [[a]]的每个元素,即[a]类型的元素。但是,traverse x的类型为[[a]] -> [[a]],因此此处的不匹配traverse期待[[a]],但您给它[a]

答案 1 :(得分:5)

一些指示:

  • 尽管可能有教育意义,但您不需要自己的追加功能 - 您可以使用++
  • comb您可能需要的案例多于必要的案例 - 您真的需要的不仅仅是comb []comb (x:xs)吗?
  • 如上所述,traverse与包含多个列表的第二个参数
  • 不匹配

我自己没有通过Haskell运行,但我认为最后一点可能是你的主要问题。


编辑:@Kevin Ballard的答案当然是正确的,为什么你会得到特定的类型错误。但是,我认为更大的问题是,在某些时候,你需要concat - enate(即,flatten)一系列组合列表(实际上是排列,如果我理解你现有的代码),我在这里看不到。

作为这个硬币的另一面,也许遍历的类型签名应该是a -> [a] -> [[a]]

答案 2 :(得分:3)

我的建议是保持具体。类型变量a可以匹配Int,[Int],[[Int]]等,这可能导致在开发的早期阶段出现混淆。一旦程序适用于具体类型,通常不会很难概括为任意类型。

以下是您之前(错误)计划的混乱版本:

append :: [a] -> [a] -> [a]
append xs ys = foldr (:) ys xs

traverse :: Int -> [Int] -> [[Int]]
traverse x [] = [[x]]
traverse x (y:ys) = append [(x:y:ys)] (map (y:) (traverse x ys))

comb :: [Int] -> [[Int]]
comb [] = [[]]
comb (x:[]) = [[x]]
comb (x:y:[]) = [[x,y],[y,x]] 
comb (x:xs) = map (traverse x) (comb xs)

ghci会抱怨最后一行:

Couldn't match expected type `Int' with actual type `[Int]'
Expected type: [Int] -> [Int]
  Actual type: [Int] -> [[Int]]
In the return type of a call of `traverse'
In the first argument of `map', namely `(traverse x)'

看起来比你遇到的更容易理解。虽然[a]可以表示来自[Int],[[Int]]等的任何内容,[Int]可以表示..嗯,[Int]。
正如你在上一个问题中所说,遍历函数很好:

Main> map (traverse 3) [[1,2],[2,1]]
[[[3,1,2],[1,3,2],[1,2,3]],[[3,2,1],[2,3,1],[2,1,3]]]

类型:

Main> :type map (traverse 3) [[1,2],[2,1]]
map (traverse 3) [[1,2],[2,1]] :: [[[Int]]]

现在,回想一下梳子功能的类型:

comb :: [Int] -> [[Int]]

类型错误的原因应该足够清楚。您需要做的就是在最后一行“合并”地图的结果,如下所示:

comb (x:xs) = concat $ map (traverse x) (comb xs)

这是(固定)程序的输出:

Main>  comb [1,2,3]
[[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]] 

现在你可以尝试推广到任意类型。