我正在尝试编写一个接受谓词和列表的函数,并返回一个满足谓词的列表。
所以,例如,我想要这样的东西:
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
我的问题是,如何使用谓词测试此函数?
答案 0 :(得分:4)
为此目的有一个函数filter
。无论如何,要测试count_if或filter,你可以做类似
filter (>3) [2,3,4,5,6]
答案 1 :(得分:3)
你的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
放在x:
的前面,让我们添加一个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
和length
等标准功能可能会导致您自己无法发现的性能增强。如果您针对countif (>0) [1..1000000]
对count_if (>0) [1..1000000]
进行测试,您会发现它的运行速度明显加快。因此,从前奏中了解诸如filter
,foldr
,scanr
之类的前奏函数是一个好主意。
答案 2 :(得分:2)
有几种方法可以编写谓词。有一些内置函数是谓词,例如:
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]
答案 3 :(得分:1)
该过滤器的现有功能
请参阅: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
如果你指定函数的类型会有帮助,那么编译器会帮助你