我有一个非常令人困惑的问题需要处理。
以下代码在我将其直接粘贴到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.
答案 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 = ...
这是我们必须处理的下一个案例,并给出了cond
,x
和xs
,除了申请之外我们可以做的事情不多cond
到x
。如果cond x
为False
,我们希望该函数尽早退出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
后立即将计算短路。
对比all
中Prelude
的定义:
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