Haskell - “应用程序中的类型错误”:“统一会给出无限类型”

时间:2012-12-19 18:56:47

标签: haskell

我正在开始使用Haskell,但是我试图制作的这种相等检查并没有成功。

我有一个函数countLetter a [b] c,其中a是char,b是字符列表,c是int。 (类型声明通过罚款。)但是我遇到了这个表达式的问题:

if a == head(b)

给我以下信息:

Type error in application

*** Expression     : a == head b
*** Term           : a
*** Type           : [a]
*** Does not match : a
*** Because        : unification would give infinite type

如果需要,我的代码是完整的:

countLetter :: char -> [char] -> int

countLetter a [b] c = if null b

                       then []
                       else
                       if a == head(b)
                       then countLetter a tail(b) c+1
                    else
                    countLetter head(b) tail(b) c

非常感谢任何帮助或建议。谢谢。

3 个答案:

答案 0 :(得分:9)

首先,Haskell中的类型以大写字母开头。如果在类型签名中使用以小写字母开头的标识符,则将它们解释为类型变量。因此,您的类型char -> [char] -> inta -> [a] -> b相同,这对您的函数来说不是一个明智的类型。您想要Char -> [Char] -> Int

其次[]不是Int类型的有效值。修复您的类型签名应该会产生一条错误消息,告诉您更不明确的条款。

然后它还应该告诉您null b是类型错误,因为b的类型为Char(函数的第二个参数类型为[Char]而您已经将它与模式[b]匹配,将b绑定到该列表中包含的单个Char,并且null将列表而不是Char作为其参数。

稍微澄清最后一点:

你的函数的第二个参数是一个列表。通过在参数列表中编写[b],您可以将该参数与模式[b]进行匹配。换句话说,写countLetter a [b] c = blabla与写作相同:

countLetter a theList c =
    case theList of
        [b] -> blabla

所以你说:“函数的第二个参数必须是一个包含一个元素的列表,该元素应该被称为b”。这不是你想要说的。你想要说的是“函数的第二个参数(顺便提一下,它是一个列表)应该被称为b”。为此,您只需编写countLetter a b c = blabla

类型列表的参数不必与其他类型的参数进行任何不同的表示。

答案 1 :(得分:2)

您将遇到的另一个错误是:

countLetter head(b) tail(b) c

根据规则,这与

相同
countLetter head b tail b c

即。你用count参数调用你的countLetter函数,只需要3个(根据第一个等式)或2个(根据你的类型签名)。 (这是编译器非常不满意的另一点。)

你可能想要这个:

countLetter (head b) (tail b) c

同样地:

countLetter a tail(b) c+1

相同
(countLetter a tail b c) + 1

但你可能想要:

countLetter a (tail b) (c+1)

答案 2 :(得分:1)

除了提供修复功能的其他答案之外,您可能还需要考虑以更具成分的方式编写它。具体来说,通过构建其他标准函数来寻找编写函数的方法。假设您有一个函数将删除列表中不等于测试字母的所有内容。

myFilter :: Char -> [Char] -> [Char
myFilter = ...

使用myFilter后,您将看到一个列表,其中只包含您要检查的元素。此时,您可以使用length来获取列表的长度:

countLetter :: Char -> [Char] -> Int
countLetter a b = length $ myFilter a b

所以现在你只需要定义myFilter,这可以使用标准Prelude函数filter

来完成
myFilter a b = filter (==a) b

对于一个小的函数,创建我们自己的定义几乎不值得,因为你可以写

countLetter a b = length $ filter (== a) b

现在,在ghci中定义它以查看它为函数countLetter

找到的类型
Prelude> let countLetter a b = length $ filter (== a) b
Prelude> :t countLetter
countLetter :: Eq a => a -> [a] -> Int

看不到Char!实现不依赖于元素是字母,只是可以比较它们的相等性(这也适用于您的方法)。所以ghci在计算的类型中反映出来。但您可以通过将Char替换为a来确定这正是您想要的类型。

许多功能程序员倾向于找到这种方法,即通过组合较小的部件来构建功能,特别容易推理,因此它很常见。特别是在使用列表时,您可能希望查看是否可以使用所谓的高阶函数编写实现,例如mapfilter或折叠,而不是使用递归。有时递归是最清晰的方式,但我希望你经常会发现函数组合是一种更好的方法。