[英]Counting number of elements in a list that satisfy the given predicate
Does Haskell standard library have a function that given a list and a predicate, returns the number of elements satisfying that predicate? Haskell 标准库是否有一个 function 给定一个列表和一个谓词,返回满足该谓词的元素数? Something like with type
(a -> Bool) -> [a] -> Int
.类似于类型
(a -> Bool) -> [a] -> Int
。 My hoogle search didn't return anything interesting.我的 hoogle 搜索没有返回任何有趣的东西。 Currently I am using
length. filter pred
目前我正在使用
length. filter pred
length. filter pred
, which I don't find to be a particularly elegant solution. length. filter pred
,我认为这不是一个特别优雅的解决方案。 My use case seems to be common enough to have a better library solution that that.我的用例似乎很常见,可以有一个更好的库解决方案。 Is that the case or is my premonition wrong?
是这样还是我的预感错了?
The length . filter p
length . filter p
length . filter p
implementation isn't nearly as bad as you suggest. length . filter p
实现并不像你建议的那么糟糕。 In particular, it has only constant overhead in memory and speed, so yeah.特别是,它在内存和速度方面只有恒定的开销,所以是的。
For things that use stream fusion, like the vector
package, length . filter p
对于使用流融合的东西,比如
vector
包, length . filter p
length . filter p
will actually be optimized so as to avoid creating an intermediate vector. length . filter p
实际上会被优化以避免创建一个中间向量。 Lists, however, use what's called foldr/build
fusion at the moment, which is not quite smart enough to optimize length . filter p
然而,列表目前使用所谓的
foldr/build
fusion ,这不够聪明,无法优化length . filter p
length . filter p
without creating linearly large thunks that risk stack overflows. length . filter p
而不创建有堆栈溢出风险的线性大 thunk。
For details on stream fusion, seethis paper .有关流融合的详细信息,请参阅本文。 As I understand it, the reason that stream fusion is not currently used in the main Haskell libraries is that (as described in the paper) about 5% of programs perform dramatically worse when implemented on top of stream-based libraries, while
foldr/build
optimizations can never (AFAIK) make performance actively worse.据我了解,目前在主要的 Haskell 库中不使用流融合的原因是(如论文中所述)在基于流的库之上实现时,大约 5% 的程序的性能要差得多,而
foldr/build
优化永远不会(AFAIK)使性能主动变差。
No, there is no predefined function that does this, but I would say that length . filter pred
不,没有预定义的函数可以做到这一点,但我会说
length . filter pred
length . filter pred
is, in fact, an elegant implementation; length . filter pred
实际上是一个优雅的实现; it's as close as you can get to expressing what you mean without just invoking the concept directly, which you can't do if you're defining it.它尽可能接近表达您的意思,而无需直接调用概念,如果您正在定义它,则无法做到这一点。
The only alternatives would be a recursive function or a fold, which IMO would be less elegant, but if you really want to:唯一的选择是递归函数或折叠,IMO 会不太优雅,但如果你真的想要:
foo :: (a -> Bool) -> [a] -> Int
foo p = foldl' (\n x -> if p x then n+1 else n) 0
This is basically just inlining length
into the definition.这基本上只是将
length
内联到定义中。 As for naming, I would suggest count
(or perhaps countBy
, since count
is a reasonable variable name).至于命名,我建议
count
(或者可能countBy
,因为count
是一个合理的变量名)。
Haskell is a high-level language. Haskell 是一种高级语言。 Rather than provide one function for every possible combination of circumstances you might ever encounter, it provides you with a smallish set of functions that cover all of the basics, and you then glue these together as required to solve whatever problem is currently at hand.
它不是为您可能遇到的每种可能的情况组合提供一个函数,而是为您提供一组涵盖所有基础知识的小函数,然后您可以根据需要将它们粘合在一起以解决当前手头的任何问题。
In terms of simplicity and conciseness, this is as elegant as it gets.就简单性和简洁性而言,这是最优雅的。 So yes,
length . filter pred
所以是的,
length . filter pred
length . filter pred
is absolutely the standard solution. length . filter pred
绝对是标准解决方案。 As another example, consider elem
, which (as you may know) tells you whether a given item is present in a list.再举一个例子,考虑
elem
,它(你可能知道)告诉你一个给定的项目是否存在于列表中。 The standard reference implementation for this is actually对此的标准参考实现实际上是
elem :: Eq x => x -> [x] -> Bool elem x = foldr (||) False . map (x ==)
In order words, compare every element in the list to the target element, creating a new list of Bools.换句话说,将列表中的每个元素与目标元素进行比较,创建一个新的布尔列表。 Then fold the logical-OR function over this new list.
然后在这个新列表上折叠逻辑或函数。
If this seems inefficient, try not to worry about it.如果这看起来效率低下,请尽量不要担心。 In particular,
特别是,
The compiler can often optimise away temporary data structures created by code like this.编译器通常可以优化掉由这样的代码创建的临时数据结构。 (Remember, this is the standard way to write code in Haskell, so the compiler is tuned to deal with it.)
(请记住,这是在 Haskell 中编写代码的标准方式,因此编译器会进行调整以处理它。)
Even if it can't be optimised away, laziness often makes such code fairly efficient anyway.即使它不能被优化掉,懒惰通常会使这样的代码无论如何都相当有效。
(In this specific example, the OR function will terminate the loop as soon as a match is seen - just like what would happen if you hand-coded it yourself.) (在此特定示例中,OR 函数将在看到匹配项后立即终止循环 - 就像您自己手动编码时会发生的情况一样。)
As a general rule, write code by gluing together pre-existing functions.作为一般规则,通过将预先存在的函数粘合在一起来编写代码。 Change this only if performance isn't good enough.
仅当性能不够好时才更改此设置。
This is my amateurish solution to a similar problem.这是我对类似问题的业余解决方案。 Count the number of negative integers in a list l
计算列表中负整数的个数 l
nOfNeg l = length(filter (<0) l)
main = print(nOfNeg [0,-1,-2,1,2,3,4] ) --2
As of 2020, there is indeed no such idiom in the Haskell standard library yet!到 2020 年,Haskell 标准库中确实还没有这样的习惯用法! One could (and should) however insert an idiom
howMany
(resembling good old any
)然而,人们可以(并且应该)插入一个习语
howMany
(类似于 good old any
)
howMany p xs = sum [ 1 | x <- xs, p x ]
-- howMany=(length.).filter
main = print $ howMany (/=0) [0..9]
I'd do manually我会手动做
howmany :: (a -> Bool) -> [a] -> Int
howmany _ [ ] = 0
howmany pred (x:xs) = if pred x then 1 + howmany pred xs
else howmany pred xs
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.