我正在尝试编写一个函数,该函数接受谓词和列表并返回满足谓词的列表。
所以,例如,我想要这样的东西:
haskell> count_if (x > 3) [2,3,4,5,6]
[4,5,6]
这是我到目前为止所拥有的:
count_if f [] = 0
count_if f (x:xs)
| f x = x : count_if f xs
| otherwise = count_if f xs
我的问题是,如何使用谓词测试这个函数?
filter
为此目的有一个功能。无论如何,要测试 count_if 或过滤器,您可以执行类似的操作
filter (>3) [2,3,4,5,6]
您的countif
函数正在努力计算任何内容,因为您告诉它列出一个列表:
count_if f [] = 0 -- fine, makes sense
count_if f (x:xs)
| f x = x : count_if f xs -- Oops, no!
| otherwise = count_if f xs -- yup
请注意1:[2,3]
= [1,2,3]
,所以:
用于在列表的前面放置一个额外的元素。如果你想数数,你需要一个数字,而不是一个列表。(放在x
前面听起来很像filter
,它为您提供了谓词为真的所有元素,但您想计数,这是不同的。)
如果您通过给出明确的类型签名(如count_if :: (a -> Bool) -> [a] -> Int
. 不是把x
with 放在前面x:
,让我们加上一个 with 1+
,给
count_if :: (a -> Bool) -> [a] -> Int
count_if f [] = 0
count_if f (x:xs)
| f x = 1 + count_if f xs -- adds one to the total from the rest
| otherwise = count_if f xs
现在可以这样测试:
> count_if (>5) [1..10]
5
> count_if (=='e') "Time is of the essence"
5
> count_if even [1..100]
50
现在您可以使用count_if
. filter
过滤器的类型是filter :: (a -> Bool) -> [a] -> [a]
,它只提供您需要的元素:
> filter (>5) [1..10]
[6,7,8,9,10]
> filter (=='e') "Time is of the essence"
"eeeee"
但然后对结果做长度:
countif' :: (a -> Bool) -> [a] -> Int
countif' f xs = length (filter f xs)
但这可以写得更简洁
countif :: (a -> Bool) -> [a] -> Int
countif f = length . filter f
因为.
是函数组合 - 这表示过滤器f
,然后取长度。
(Pointfree 极客们更愿意写这个,countif = (length.).filter
但这是另一天的教训!)
filter
使用像and这样的标准函数length
可能会导致您自己可能无法发现的性能增强。如果您countif (>0) [1..1000000]
针对 进行测试count_if (>0) [1..1000000]
,您会发现它的运行速度明显更快。因此,从 prelude中了解诸如filter
、等前奏函数是一个好主意。foldr
scanr
有几种方法可以编写谓词。有一些内置函数是谓词,例如:
even :: Integer -> Bool
odd :: Integer -> Bool
null :: [a] -> Bool
您可以将运算符部分与比较运算符一起使用:
(== 0) :: Integer -> Bool
(3 >) :: Integer -> Bool
或者,您可以使用 lambda 表达式编写更复杂的谓词:
(\x -> 1 < x && x < 5) :: Integer -> Bool
例如,你应该有:
count_if even [1,2,3,4,5,6] --> [2,4,6]
count_if (3 >) [1,2,3,4,5,6] --> [1,2]
count_if (\x -> 1 < x && x < 5) [1,2,3,4,5,6] --> [2,3,4]
有一个称为过滤器的现有功能
见:http ://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:filter
编辑:
所以问题是你的实现几乎是正确的:
count_if f [] = 0
需要是
count_if :: (a -> Bool) -> [a] -> [a]
count_if f [] = []
count_if f (x:xs)
| f x = x:count_if f xs
| otherwise = count_if f xs
如果您指定函数的类型,它会有所帮助,然后编译器会帮助您