haskell - 另一种无限类型的错误

时间:2012-01-30 14:24:07

标签: haskell types type-inference

我正试图从九十九个haskell问题中解决problem 10。 这是我认为正确的解决方案。

包装功能(问题9)已经正确。问题在于编码功能。

pack :: (Eq a) => [a] -> [[a]]
pack [] = []
pack (x:xs) = (x : takeWhile (== x) xs) : (pack $ dropWhile (== x) xs)

encode :: (Eq a) => [a] -> [(Int, a)]
encode [] = []
encode list = (encode' $ head packed) : (encode $ tail packed)
    where packed = pack list
          encode' l = (length l, head l) 

当我从ghci加载文件时,这是错误:

  

encode.hs:6:0:       发生检查:无法构造无限类型:a = [a]       在推广`encode'的类型时

第6行是包含encode [] = []

的行

我的编码功能出了什么问题?我一直在检查所用变量的类型,我相信没有错。

函数用法示例(假设代码工作正常):

pack "aaaabbbbccccccddddddd"
> ["aaaa","bbbb","cccccc","ddddddd"]
encode "aaaabbbbccccccddddddd"
> [(4,'a'),(4,'b'),(6,'c'),(7,'d')]

5 个答案:

答案 0 :(得分:4)

递归在这里有些笨拙。使用更高阶函数要好得多。

您已经有一个函数encode' :: [a] -> (Int, a)来编码一个子列表,并且您希望对所有子列表进行编码。将函数应用于列表的每个元素是一种非常常见的模式,它由高阶函数map :: (a -> b) -> [a] -> [b]封装。

利用map,我们可以简单地写一下:

encode :: (Eq a) => [a] -> [(Int, a)]
encode list = map encode' $ pack list
   where encode' xs = (length xs, head xs)

您还可以使用列表解析来避免辅助函数:

encode :: (Eq a) => [a] -> [(Int, a)]
encode list = [(length xs, head xs) | xs <- pack list]

通常,尝试在适当的地方使用现有的高阶函数,而不是自己进行递归。它更具可读性,更不容易出错。

答案 1 :(得分:3)

encode $ tail packed

我们有

packed :: [[a]]
tail packed :: [[a]]

但我们需要将[a]传递给encode

(想想这样:list需要通过packpackedpack的输出,但在递归调用中它会被传递给再次pack。)

答案 2 :(得分:2)

您的问题是函数encode需要“解压缩”列表,但您传递的是“打包”列表。

添加类型签名对此有很大帮助,我为encode'

添加了类型签名
{-# LANGUAGE ScopedTypeVariables #-}

pack :: (Eq a) => [a] -> [[a]]
pack [] = []
pack (x:xs) = (x : takeWhile (== x) xs) : (pack $ dropWhile (== x) xs)

encode :: forall a. (Eq a) => [a] -> [(Int, a)]
encode [] = []
encode list = (encode' $ head packed) : (encode $ tail packed)
    where packed = pack list
          encode' :: [a] -> (Int, a)
          encode' l = (length l, head l)

,编译器快速找到错误:

[1 of 1] Compiling Main             ( test.hs, interpreted )

test.hs:9:42:
    Couldn't match type `a' with `[a]'
      `a' is a rigid type variable bound by
          the type signature for encode :: Eq a => [a] -> [(Int, a)]
          at test.hs:8:1
    Expected type: [(Int, a)]
      Actual type: [(Int, [a])]
    In the second argument of `(:)', namely `(encode $ tail packed)'
    In the expression: (encode' $ head packed) : (encode $ tail packed)
Failed, modules loaded: none.

因为仅当a[a]相同且因此与[[a]]等相同时才会有效。这是无限类型错误。或者只是样本中“打包”列表([[a]])和“解压缩”列表([a])之间的差异。

(为了更好地理解:“打包”列表是应用pack函数后的列表;-))

编辑:修复了ScopedTypeVariables与ExistentialQuantification错误,感谢John L

答案 3 :(得分:1)

您可以对要打包的结果进行模式匹配,而不是使用headtail。 然后它看起来像这样:

encode :: (Eq a) => [a] -> [(Int, a)]
encode [] = []
encode list = encode' x : encode xs
   where (x:xs)    = pack list
         encode' l = (length l, head l)

来自xs的类型错误属于[[a]]类型,因为pack 返回[[a]],但encode需要[a]参数。

答案 4 :(得分:1)

我同意@hammar的观点,即高阶函数是处理这个问题的好方法 以下是对所发生情况的一般性解释:

每当我遇到无限类型错误时,它都有以下形式:
我有一个自我调用的函数,但用比我们传递的“更深”*类型调用自己。

让我们做一个简单的无限类型错误:

*Main> let f (_:xs) = f [xs]

<interactive>:1:19:
    Occurs check: cannot construct the infinite type: t0 = [t0]

让我们分解原因:无法确定f的类型:如果f :: [a] -> [[a]],那么f [xs] :: [[a]] -> [[[a]]]会传递给原f,它无法返回[[[a]]]


*我对“更深层”的定义:
[[a]][a]更“深” Constructor (Constructor a)Constructor a

“更深”