简体   繁体   English

计算列表中满足给定谓词的元素数

[英]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,特别是,

  1. 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 中编写代码的标准方式,因此编译器会进行调整以处理它。)

  2. 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

No, there isn't!不,没有!

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]

Try howMany=(length.).filter 试试 howMany=(length.).filter

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM