Haskell中的空白错误?

时间:2014-05-20 13:10:10

标签: haskell

我有一个非常令人困惑的问题需要处理。

以下代码在我将其直接粘贴到ghci控制台时运行正常,但在从文件加载时无法运行。

all' :: (a->Bool) -> [a] -> Bool
all' cond list
    | filter cond list == list = True
    | otherwise = False

我收到以下错误消息:

5-1.hs:11:28:
    No instance for (Eq a) arising from a use of `=='
    Possible fix:
      add (Eq a) to the context of
        the type signature for all' :: (a -> Bool) -> [a] -> Bool
    In the expression: filter cond list == list
    In a stmt of a pattern guard for
                   an equation for all':
      filter cond list == list
    In an equation for all':
        all' cond list
          | filter cond list == list = True
          | otherwise = False
Failed, modules loaded: none.

2 个答案:

答案 0 :(得分:5)

我猜你一次用多个let s将它输入GHCi一行,比如

> let all' :: (a -> Bool) -> [a] -> Bool
> let all' cond list = if filter cond list == list then True else False

这不起作用,因为这两个声明彼此分开执行,后者覆盖前者。您可以做的是打开GHCi中的多行输入

> :set +m
> let all' :: (a -> Bool) -> [a] -> Bool
|     all' cond list
|         | filter cond list == list = True
|         | otherwise = False

此时您会遇到同样的错误。

您看到的错误是因为您正在使用==将过滤器的结果与原始列表进行比较,这需要比较过滤列表和原始列表的每个元素。为了做到这一点,该列表的元素必须属于实现Eq类型类的类型(从OOP思考接口,它是类似的)。但是,在函数的类型签名中,您已指定a不属于任何类型类,它可以是任何类型。要解决此问题,只需将Eq a添加到类型签名的上下文

即可
--            v  This is the context
> let all' :: Eq a => (a -> Bool) -> [a] -> Bool
>     all' cond list = ...

但是,这不是实施all的最佳方式。首先,guards / if声明是不必要的。您计算的是Bool,如果它是True,则返回True,如果它是False则返回False,这只是在进行额外的计算那里不需要,只需返回你计算的Bool

all' cond list = filter cond list == list

这仍然存在您必须在类型签名的上下文中指定Eq的问题。显然,这并不理想,因为我们希望能够在函数列表或其他任何类型的函数上调用all'。那么我们如何从定义中删除==?最简单的方法是比较长度而不是元素:

all' cond list = length (filter cond list) == length list

虽然这可以工作并具有所需的类型,但它仍然不是最有效的解决方案。如果列表中的第一个元素未通过cond怎么办?理想情况下,一旦元素失败,我们就会退出计算。相反,我们可以修改all'的定义,以便它知道列表中的各个元素:

all' cond [] = True

首先,声明all' cond []总是True,从技术上讲,空列表的所有元素都会传递cond,因为没有元素可以使它失败。下一个:

all' cond (x:xs)
    | cond x   = ...
    | otherwse = ...

这是我们必须处理的下一个案例,并给出了condxxs,除了申请之外我们可以做的事情不多condx。如果cond xFalse,我们希望该函数尽早退出False

all' cond (x:xs)
    | cond x   = ...
    | otherwse = False

如果x通过cond,我们希望继续检查列表的其余部分:

all' cond (x:xs)
    | cond x   = all' cond xs
    | otherwse = False

完整的定义:

all' :: (a -> Bool) -> [a] -> Bool
all' cond [] = True
all' cond (x:xs)
    | cond x   = all' cond xs
    | otherwse = False

我们也可以把它写成

all' cond [] = True
all' cond (x:xs) = cond x && all' cond xs

由于&&运算符会在看到False后立即将计算短路。

对比allPrelude的定义:

and xs = foldr (&&) True xs
all cond xs = and $ map cond xs

(注意:Prelude中的定义使用这些函数的无点版本)。这更简单但实现了同样的目的。 foldr函数仅用于推广这种递归模式。这里可能有点困难,但all'只是将cond应用于列表的每个元素,然后对这些结果进行逻辑AND。

答案 1 :(得分:0)

您在

中使用==
filter cond list == list = True

表示a必须实现Eq,即您的类型签名应为:

all' :: Eq a => (a->Bool) -> [a] -> Bool