mis*_*tor 39 haskell functional-programming
Haskell标准库是否具有给定列表和谓词的函数,返回满足该谓词的元素数量?类似于类型的东西(a -> Bool) -> [a] -> Int
.我的搜索没有回复任何有趣的东西.目前我正在使用length . filter pred
,我发现这并不是一个特别优雅的解决方案.我的用例似乎很常见,有一个更好的库解决方案.是这种情况还是我的预感错了?
Lou*_*man 41
该length . filter p
执行不按照你的建议几乎一样糟糕.特别是,它在内存和速度方面只有不断的开销,所以是的.
对于使用流融合的东西,如vector
包,length . filter p
将实际上进行优化,以避免创建中间向量.然而,列表使用目前所谓的foldr/build
融合,这不够智能,length . filter p
无法创建线性大的风险堆栈溢出.
有关流融合的详细信息,请参阅此文章.据我了解,目前在主Haskell库中没有使用流融合的原因是(如本文所述),当基于流的库实现时,大约5%的程序执行得更糟,而foldr/build
优化永远不会(AFAIK)使表现更加恶化.
不,没有预定义的功能可以做到这一点,但我想说的length . filter pred
是,事实上,这是一个优雅的实现; 它就像你能够直接调用概念一样表达你的意思,如果你定义它就不能做到.
唯一的选择是递归功能或折叠,IMO不那么优雅,但如果你真的想:
foo :: (a -> Bool) -> [a] -> Int
foo p = foldl' (\n x -> if p x then n+1 else n) 0
Run Code Online (Sandbox Code Playgroud)
这基本上只是内联length
到定义中.至于命名,我建议count
(或者,或许countBy
,因为count
是一个合理的变量名称).
Haskell是一种高级语言.它不是为您可能遇到的每种可能情况组合提供一种功能,而是为您提供涵盖所有基础知识的一小组功能,然后根据需要将这些功能粘合在一起以解决当前存在的任何问题.
在简洁和简洁方面,这是优雅的.所以,是的,length . filter pred
是绝对标准的解决方案.作为另一个例子,考虑一下elem
(如您所知),它会告诉您列表中是否存在给定项目.实际上,标准参考实现是这样的
elem :: Eq x => x -> [x] -> Bool elem x = foldr (||) False . map (x ==)
换句话说,将列表中的每个元素与目标元素进行比较,创建一个新的Bools列表.然后将逻辑或函数折叠在此新列表上.
如果这看起来效率低下,请不要担心.特别是,
编译器通常可以优化由这样的代码创建的临时数据结构.(请记住,这是在Haskell中编写代码的标准方法,因此编译器已经过调整以处理它.)
即使它无法被优化掉,懒惰通常也会使这些代码相当有效.
(在这个具体的例子中,OR函数会在看到匹配时立即终止循环 - 就像你自己手动编码一样会发生这种情况.)
作为一般规则,通过将预先存在的函数粘合在一起来编写代码.仅在性能不够好时才更改此设置.