【问题标题】:haskell - yet another infinite type errorhaskell - 另一个无限类型错误
【发布时间】:2012-02-22 07:57:38
【问题描述】:

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

pack 函数(来自问题 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] 概括“编码”的类型时

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

我的编码功能有什么问题?我不断检查使用的变量的类型,我相信没有错。

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

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

【问题讨论】:

    标签: haskell types type-inference


    【解决方案1】:

    这里的递归有点笨拙。最好使用高阶函数。

    您已经有一个函数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]
    

    一般来说,尽量在适当的地方使用现有的高阶函数,而不是自己进行递归。它更具可读性且不易出错。

    【讨论】:

    • 您的两个解决方案看起来都更干净。感谢您的建议!
    【解决方案2】:
    encode $ tail packed
    

    我们有

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

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

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

    【讨论】:

      【解决方案3】:

      您的问题是函数 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

      【讨论】:

      • 这看起来...对我来说太高级了(刚开始使用 haskell)。
      • 让我换种说法:编译器试图告诉你:“解压”列表与“打包”列表相同,但前提是[a][[a]] 相同。确实如此!查看您对pack 的定义:pack :: (Eq a) =&gt; [a] -&gt; [[a]]pack[a] 转换为 [[a]]。如果那是相同的,“打包”和“解包”列表将是相同的,并且您的程序将正常工作。但是:那么我们将拥有a == [a] == [[a]] == [[[a]]] == [[[[a]]]],因此:a 类型的“名称”数量无限。编译器告诉你infinite type error :-)
      • 现在我明白了为什么错误被称为“无限类型错误”......顺便说一句,当我说你的答案看起来太高级时,我提到了 ExistentialQuantification 的事情。我在 wikibooks 中找到了一个参考资料,它看起来……嗯……太高级了,现在:)
      • 酷:-)。现在就忽略 ExistentialQuantification 吧。它所做的只是告诉编译器函数encode 中使用的所有as 都是相同的类型。 forall a. 表示encodeencode' 的类型签名指定了一些关于任意但固定类型a 的内容。我想告诉编译器这两个as 不能是不同的类型。
      • 这个答案实际上并没有使用 ExistentialQuantification;正在使用的扩展名是 ScopedTypeVariables,这是 ExistentialQuantification 所暗示的。 ScopedTypeVariables 很简单,只是表示encode' 类型中的a 变量与encode 类型签名中的a 相同。
      【解决方案4】:

      您可以对要打包的结果进行模式匹配,而不是使用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] 参数。

      【讨论】:

      • @操作符很方便,有什么花哨的名字吗?
      • @Hery:它们被称为 as-patterns 或 @-patterns
      【解决方案5】:

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

      每次我遇到无限类型错误时,都会出现以下形式:
      我有一个调用自身的函数,但使用比传递的“更深”* 类型调用自身。

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

      *Main> let f (_:xs) = f [xs]
      
      <interactive>:1:19:
          Occurs check: cannot construct the infinite type: t0 = [t0]
      

      让我们分解原因:无法确定f 的类型:如果f :: [a] -&gt; [[a]],则f [xs] :: [[a]] -&gt; [[[a]]],它被传递给原来的f,它不能返回[[[a]]] .


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

      【讨论】:

      • 它也可以以其他方式发生,例如f (x:_) = f x
      猜你喜欢
      • 1970-01-01
      • 2012-09-29
      • 1970-01-01
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      相关资源
      最近更新 更多