當我開始嘗試讓我的頭一輪的Haskell性能,是什麼使事物快,慢,和我這個有點困惑。執行較慢使用無限列表
我有一個生成素數的列表達到一定值的函數的兩種實現。首先是直客哈斯克爾維基:
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..])
在這兩種情況下,負作用是:
minus :: (Ord a) => [a] -> [a] -> [a]
minus (x:xs) (y:ys) = case (compare x y) of
LT -> x : minus xs (y:ys)
EQ -> minus xs ys
GT -> minus (x:xs) ys
minus xs _ = xs
後者的實現比前者慢得多(〜100x),我不明白爲什麼。我會認爲哈斯克爾的懶惰評估會讓他們在引擎蓋下相當等價。
這顯然是一個簡化的測試用例 - 在現實生活中,優化是沒有問題的(雖然我不明白爲什麼需要這樣做),但對我來說,一個函數只會產生一個無限的素數列表比有限列表更普遍有用,但看起來較慢。
注意,您可以提振takeWhile出primes2的,並獲得了嘗試更寬泛的素數的功能。你可以看看生成的核心和C來查看差異,但我認爲,簡而言之,從算法上思考,不同之處在於無限列表版本做了一些「重疊」工作,使得獲得m + 1的工作比primesTo兩次,爲m,然後爲m + 1。但如果你不需要這項工作,最好不要這樣做。因此,對於這種算法,如果您事先知道您需要的最大值,則可以節省工作量。 – misterbee
作爲一個練習來找到另一個具有類似屬性的問題+算法會很有啓發。 – misterbee