Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 使用无限列表时执行速度较慢_Performance_Haskell - Fatal编程技术网

Performance 使用无限列表时执行速度较慢

Performance 使用无限列表时执行速度较慢,performance,haskell,Performance,Haskell,我开始试着了解haskell的表现,以及是什么让事情变得快和慢,我对此有点困惑 我有两个函数的实现,它生成一个素数列表,最大值为某个值。第一个是直接从Haskell wiki上下载的: primesTo :: (Ord a, Num a, Enum a) => a -> [a] primesTo m = eratos [2..m] where eratos [] = [] eratos (p:xs) = p : eratos (xs `minus` [p*p,

我开始试着了解haskell的表现,以及是什么让事情变得快和慢,我对此有点困惑

我有两个函数的实现,它生成一个素数列表,最大值为某个值。第一个是直接从Haskell wiki上下载的:

primesTo :: (Ord a, Num a, Enum a) => a -> [a]
primesTo m = eratos [2..m]  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..m])
第二个相同,但在内部使用无限列表:

primes2 :: (Ord a, Num a, Enum a) => a -> [a]
primes2 m = takeWhile (<= m) (eratos [2..])  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..])
后一种实现比前一种慢很多(~100倍),我不明白为什么。我原以为哈斯凯尔的懒惰评估会使他们在幕后相当平等


这显然是一个简化的测试用例,用于问题的目的-在现实生活中,优化是没有问题的(尽管我不理解为什么需要优化),但对我来说,只生成无限素数列表的函数比有限素数列表更通用,但处理起来似乎比较慢。

在我看来,两者之间有很大的区别

(xs `minus` [p*p, p*p+p..m])  -- primesTo
(xs `minus` [p*p, p*p+p..])   -- primes2
函数
减号
成对地遍历列表,并在一个列表到达末尾时终止。在上面的第一个
减号
表达式中,当后一个列表用尽时,这只在
(m-p*p)/p
步骤中发生。在第二种情况下,它将始终按照
length xs
的顺序执行步骤


因此,您的无限列表至少禁用了一个有意义的优化。

一个区别是,在第二种情况下,您需要生成一个额外的素数。您需要在
takeWhile
知道停止时间之前生成大于
m
的第一个素数

此外,要筛选的列表和倍数列表上的
[…m]
边界有助于减少计算数量。每当这些列表中的一个为空时,减号就会立即通过其secons子句返回,而在无限情况下,减号会在第一种情况下卡住。如果您还测试只有一个列表是无限的情况,则可以更好地探索这一点:

--this is also slow
primes3 :: (Ord a, Num a, Enum a) => a -> [a]
primes3 m = takeWhile (<= m) (eratos [2..m])  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..])

--this fast
primes4 :: (Ord a, Num a, Enum a) => a -> [a]
primes4 m = takeWhile (<= m) (eratos [2..])  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..m])
——这也很慢
primes3::(Ord a,Num a,Enum a)=>a->[a]
primes3 m=takeWhile(a->[a]

primes4 m=takeWhile(请注意,您可以提高primes2的takeWhile,并获得一个更通用的primes函数。您可以查看生成的Core和C以查看差异,但我认为简单地说,从算法角度考虑,差异在于无限列表版本有一些“重叠”使得到m+1的工作量比primesTo少两倍的工作,对于m,然后对于m+1。但是如果你不需要这项工作,最好不要这样做。因此,对于这个算法,如果你提前知道你将需要的最大m,你可以节省工作量。作为一个练习,找到另一个具有类似属性的问题+算法是很有启发性的。我理解后一个列表终止了减号的执行,当p趋向于m时,减号的计算会变短。我试图理解的是,延迟计算在何处以及何时适用于无限列表-我希望在这种情况下,减号只会被计算为满足takeWhile所需的数量(以及上面的一切)这与显式有界的情况没有太大区别。
减号不会成对地通过列表。
减号只会在头相等时从两个列表中消耗。因此在这两种情况下,步数都将接近
长度xs
。但是,在第一种情况下,您可以耗尽优先级列表mes,避免在
p
的最后倍数和下一倍数之间发出砰砰声和检查
xs
--this is also slow
primes3 :: (Ord a, Num a, Enum a) => a -> [a]
primes3 m = takeWhile (<= m) (eratos [2..m])  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..])

--this fast
primes4 :: (Ord a, Num a, Enum a) => a -> [a]
primes4 m = takeWhile (<= m) (eratos [2..])  where
   eratos []     = []
   eratos (p:xs) = p : eratos (xs `minus` [p*p, p*p+p..m])