4

我开始与 Haskell 合作,但我试图进行的这种平等检查没有奏效。

我有一个函数,countLetter a [b] c其中a是一个字符,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

任何帮助或建议将不胜感激。谢谢你。

4

3 回答 3

9

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

其次[]不是 type 的有效值Int。修复您的类型签名应该会产生一条错误消息,以不那么模棱两可的方式告诉您这一点。

然后它还应该告诉您这null b是一个类型错误,因为bis of type Char(您的函数的第二个参数是 type[Char]并且您已经将它与 pattern 匹配[b],绑定b到该列表中包含的单个Char)并null接受一个列表,而不是一个 Char,作为它的论点。

为了澄清最后一点:

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

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

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

list 类型的参数不必与其他类型的参数有任何不同的表示。

于 2012-12-19T19:00:01.533 回答
2

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

countLetter head(b) tail(b) c

根据规则,这与

countLetter head b tail b c

即你用 5 个参数调用你的 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)
于 2012-12-19T23:24:48.883 回答
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

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

于 2012-12-20T06:58:58.450 回答