1

我正在尝试编写一个函数,该函数接受谓词和列表并返回满足谓词的列表。

所以,例如,我想要这样的东西:

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 

我的问题是,如何使用谓词测试这个函数?

4

4 回答 4

4

filter为此目的有一个功能。无论如何,要测试 count_if 或过滤器,您可以执行类似的操作

filter (>3) [2,3,4,5,6]
于 2012-12-15T18:59:09.670 回答
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. 不是把xwith 放在前面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、等前奏函数是一个好主意。foldrscanr

于 2012-12-18T17:15:29.380 回答
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]
于 2012-12-15T23:12:57.913 回答
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 

如果您指定函数的类型,它会有所帮助,然后编译器会帮助您

于 2012-12-15T18:59:08.440 回答