至少,你的代碼應該是
primes = [2,3,5,7,11,13]
genPrimes primes max = go primes (length primes) 1 (last primes + 2)
where
go prs len d t
| len >= max = prs
| (prs !! d) > (floor . sqrt . fromIntegral) t
= go (prs++[t]) (len+1) 1 (t + 2)
| t `rem` (prs !! d) == 0 = go prs len 1 (t + 2)
| t `rem` (prs !! d) /= 0 = go prs len (d + 1) t
test n = print $ genPrimes primes n
main = test 20
然後你重新組織這樣(抽象出每個候選號碼進行的測試,作爲noDivs
功能):
genPrimes primes max = go primes (length primes) (last primes + 2)
where
go prs len t
| len >= max = prs
| noDivs (floor . sqrt . fromIntegral $ t) t prs
= go (prs++[t]) (len+1) (t + 2)
| otherwise = go prs len (t + 2)
noDivs lim t (p:rs)
| p > lim = True
| t `rem` p == 0 = False
| otherwise = noDivs lim t rs
那麼你重寫noDivs
爲
noDivs lim t = foldr (\p r -> p > lim || rem t p /= 0 && r) False
然後你注意到go
只是通過這樣的篩選數字,通過noDivs
測試:
genPrimes primes max = take max (primes ++ filter theTest [t, t+2..])
where
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t
但這並沒有工作,因爲theTest
需要通過primes
(全新的素數,因爲他們正在找到)到noDivs
,但我們正在建設這個whole_primes
列表(因爲take max (primes ++ ...)
),所以有一個惡性循環?不,因爲我們只測試一個數字的平方根:
genPrimes primes max = take max wholePrimes
where
wholePrimes = primes ++ filter theTest [t, t+2..]
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t wholePrimes
這是現在的工作。但最後,沒有什麼特殊的genPrimes
現在,它只是take
美化了電話,和初始primes
名單實際上可以縮小,所以我們得到(改變參數安排noDivs
一點,使其界面更加通用):
primes = 2 : 3 : filter (noDivs $ tail primes) [5, 7..]
noDivs factors t = -- return True if the supplied list of factors is too short
let lim = (floor . sqrt . fromIntegral $ t)
in foldr (\p r-> p > lim || rem t p /= 0 && r) True factors
-- all ((/=0).rem t) $ takeWhile (<= lim) factors
-- all ((/=0).rem t) $ takeWhile ((<= t).(^2)) factors
-- and [rem t f /= 0 | f <- takeWhile ((<= t).(^2)) factors]
全局primes
列表現在被無限期地定義(即「無限」)。 Next step是要認識到,在質數的連續平方之間,要測試的因子列表的長度將是相同的,對於每個新的分段遞增1。我們可以直接生成它們的倍數(因此每一個都是從它的主要因素中生成的),而不是測試每個數字是否是它是是其平方根以下任何一個素因子的倍數。
除了提供錯誤的代碼之外,發佈確切的錯誤消息通常是一個好主意。這使我們更容易幫助你。 – dave4420 2011-12-25 09:02:28