我是Haskell的初学者。我有这个功能
test f xs
| length xs == 0 = []
| f (head xs) = head xs : test f (tail xs)
| otherwise = test f (tail xs)
此函数应该从列表xs的每个元素中创建一个列表,该列表与f值匹配,但它会返回错误:
test 1 [1,2]
ERROR - Cannot infer instance
*** Instance : Num (a -> Bool)
*** Expression : test 1 [1,2]
这部分:
| f (head xs)
应该是Bool,但是呢?它是否作为过滤器工作?如何使此功能起作用? 提前谢谢。
答案 0 :(得分:5)
显然,f
是一种谓词:您将它应用于列表中的值,并产生一个布尔值。所以test
的类型是
test :: (a -> Bool) -> [a] -> [a]
BTW标准名称为filter
。
如果你自己不确定签名,你可以问GHCi
> :t test
test :: (a -> Bool) -> [a] -> [a]
...但我强烈建议使用签名启动:首先考虑一下你希望你的功能完成什么,然后担心实现。
无论如何,为了测试test
函数,你需要给它一个(a -> Bool)
函数和一个列表。 1
是一个函数吗?我不敢†,因此test 1 [1,2]
无法理解。你的意思可能是test (\x -> x==1) [1,2]
,也可以简单地写成test (==1) [1,2]
。
关于您的代码的几点评论:
length
- 这需要遍历整个列表。你可以使用null
函数,它就是这样做 - 检查容器是否为空...... ...但是仍然存在实际从列表中取出值的问题。您确实可以使用head
获取非空列表的第一个值,但这只是在您第一次检查它是非空的分支中是安全的。这很容易出错。一个更好的解决方案是在列表头上模式匹配:而不是
test f xs
| length xs == 0 = []
| f (head xs) = ...
写
test f [] = []
test f (x:xs)
| f x = ...
这样,无法错误地评估空列表的头部,因为编译器确保x
仅在已检查为非空的子句的范围内
查看标准filter
函数的the source code以供最终参考。
† 实际上,1
可以是一个函数...数字文字在Haskell中被重载;你理论上可以写
instance Num (Integer -> Bool) where
fromInteger n x = x == n
然后您可以定义f = 1 :: Integer -> Bool
,并将其用于test
。但是......这将是一个非常糟糕的主意;这样的实例会造成非常混乱的代码。