Haskell 哈斯克尔';计数出现次数';功能

Haskell 哈斯克尔';计数出现次数';功能,haskell,Haskell,我在Haskell中实现了一个count函数,我想知道这在大型列表中是否会表现糟糕: count :: Eq a => a -> [a] -> Int count x = length . filter (==x) 我相信length函数是以线性时间运行的,对吗 编辑:重构@Higemaru建议的长度以线性时间运行到列表的大小,是的 通常,您会担心您的代码必须经过两次列表:第一次是过滤,然后是计算结果列表的长度。然而,我相信这不会发生在这里,因为过滤器对列表的结构并不严

我在Haskell中实现了一个
count
函数,我想知道这在大型列表中是否会表现糟糕:

count   :: Eq a => a -> [a] -> Int
count x =  length . filter (==x)
我相信
length
函数是以线性时间运行的,对吗


编辑:重构@Higemaru建议的长度以线性时间运行到列表的大小,是的


通常,您会担心您的代码必须经过两次列表:第一次是过滤,然后是计算结果列表的长度。然而,我相信这不会发生在这里,因为过滤器对列表的结构并不严格。相反,length函数强制过滤列表中的元素,一次完成实际计数。

这实际上取决于列表。在我的计算机上,对于一个正常的、经过延迟计算的
Int
s列表,我看到这个函数在大约2秒的时间内运行10^9个元素,0.2秒运行10^8个元素,0.3秒运行10^7个元素,所以它似乎是以线性时间运行的。当从命令行运行可执行文件时,您可以通过将标志
+RTS-s-RTS
传递给可执行文件来检查这一点

我还试着用更多的内核运行它,但它似乎除了增加一点内存使用之外什么也做不了


延迟计算的另一个好处是,您只需通过列表一次<代码>过滤器和
长度
由编译器转换为单个循环(启用了优化),因此可以节省内存和效率。

我认为可以稍微缩短一点

count :: Eq a => a -> [a] -> Int
count x = length . filter (x==)

(如果可以的话,我会写一篇(低俗的)评论)

我认为这不是懒惰,而是流融合。如果你看一下
-ddump siml
的输出,在
-O0
它是两个独立的调用,在
-O2
它是一个单独的工作进程。嗯,即使没有流融合,它也会是一个过程,仅仅因为过滤器是懒惰的吗?也许我只是误解了它的评估方法。是的,即使没有融合,它也是一个单一的过程。融合只会有助于消除该过程中的内存分配。“这真的取决于列表”——例如?什么是不是“正常、懒散评估”的列表?即使您的意思是“一个先前评估过的列表”,那么它仍然应该具有线性运行时。(图形缩减比应用程序顺序缩减做的工作更少——问题通常是空间泄漏。)至于使用更多内核运行,Haskell不会自动并行顺序单线程代码。无论如何,列表遍历都需要线性时间,所以您最多可以期望一个常数因子。最后,懒惰仍然是一种胜利(在严格的语言中,你不会强迫中间列表)…不管你是否开启优化。很抱歉听起来很挑剔——你的帖子有点模糊。让我们不要让Haskell的表演变得比它必须的更黑暗:)另一方面,你的经验方法也是非常必要的…@Fixnum好吧,你可以使用向量(装箱和未装箱)、预排序列表和各种其他类似列表的数据结构,而不是使用GHC的内置列表类型。我也知道GHC(不是Haskell,因为我们是技术性的)不能并行化所有的东西,但我编写的算法在
-线程化
+RTS-N-RTS
中的性能大不相同,即使没有任何显式并行。最后,我添加了关于打开优化的评论,因为我不确定GHC与关闭它们相比可能做的所有优化。抱歉说得太模糊了。如果你真的有妄想症,你也可以做一个相当于一个循环的动作:
count needle haystack=foldl'(\acc elem->If elem==needle then acc+1 else acc)0 haystack
。你不能比O(n)更快地计算n个项目,所以我不知道你担心的是什么。@augustss-我担心的是如果我在一个列表上做两次传递。@Decentral1由于懒惰,它会表现得更像一次传递(长度)。过滤器。(==) ;) 哈,只是开玩笑。@Jepcats我不能让它在ghci中交互输入时工作。使用正确的类型签名(并从磁盘上的文件加载),它可以工作,但下面的不是很奇怪吗<代码>前奏曲>:t(长度)。过滤器。(==)
(长度)。过滤器。(==)::等式a=>a->[a]->Int
Prelude>let count=(长度)。过滤器。(==)
Prelude>:t count
count::()->[()]->Int
(抱歉,似乎无法正确设置格式)这很奇怪。我在我的GHCI中看不到同样的事情。我可以键入以下命令:
λ>:t(长度)。过滤器。(==)
输出
(长度)。过滤器。(==)::等式a=>a->[a]->Int
以及
λ>let count=(长度)。过滤器。(==)
后接
λ>:t count
,按预期输出
count::Eq a=>a->[a]->Int
。值得注意的是,如果我将count的无点定义放在一个.hs文件中,它在没有类型签名的情况下无法编译。@Jepcats很抱歉在这么长时间后回复您。我得到
count::()->[()]->Int
你得到
count::Eq a=>a->[a]->Int
。我的GHCi版本似乎是7.6.3。无论如何,谢谢你的意见。