素数のリストをHaskellに実装して、それらを怠惰に取得できるようにするにはどうすればよいでしょうか?
Haskellは初めてですが、遅延評価機能の実用的な使い方を学びたいと思います。
これは、 Literate Programs: から素数を列挙する短いHaskell関数です。
primes :: [Integer]
primes = sieve (2 : [3, 5..])
where
sieve (p:xs) = p : sieve [x|x <- xs, x `mod` p > 0]
どうやら、これはnotエラトステネスのふるいです(ありがとう、Landei)。 Haskellで非常にエレガントで短いコードを書くことができ、間違ったデータ構造を選択すると効率が大幅に低下する可能性があることを示す、これはまだ有益な例だと思います。
このペーパーから実装の1つを取ることをお勧めします: http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
Haskell wikiには、素数シーケンスを遅延生成するためのソリューションがいくつかあります。最初で最も単純なのは 延期されたターナーふるい : (古いリビジョン... NB)
primes :: [Integer]
primes = 2: 3: sieve (tail primes) [5,7..]
where
sieve (p:ps) xs = h ++ sieve ps [x | x <- t, x `rem` p /= 0]
-- or: filter ((/=0).(`rem`p)) t
where (h,~(_:t)) = span (< p*p) xs
@nikieから受け入れられた回答はあまり効率的ではなく、数千を超えると比較的遅くなりますが、@ sleepynateの回答ははるかに優れています。それを理解するのに少し時間がかかりました。したがって、ここに同じコードがありますが、変数の名前がより明確になっています。
lazyPrimes :: [Integer]
lazyPrimes = 2: 3: calcNextPrimes (tail lazyPrimes) [5, 7 .. ]
where
calcNextPrimes (p:ps) candidates =
let (smallerSquareP, (_:biggerSquareP)) = span (< p * p) candidates in
smallerSquareP ++ calcNextPrimes ps [c | c <- biggerSquareP, rem c p /= 0]
主な考え方は、次の素数の候補には、関数に与えられた最初の素数よりも小さい素数で割り切れる数がすでに含まれていないということです。だからあなたが呼ぶなら
calcNextPrimes (5:ps) [11,13,17..]
候補リストには番号が含まれていません。つまり、2
または3
で割り切れます。つまり、最初の非素数候補は5 * 5
になり、5* 2
と5 * 3
および5 * 4
はすでに削除されています。これにより、5の平方根よりも小さいすべての候補を取得し、それらをすぐに素数に追加し、残りをふるいにかけて5で割り切れるすべての数を削除することができます。
_primes = 2 : [x | x <- [3..], all (\y -> x `mod` y /= 0)
(takeWhile (<= (floor . sqrt $ fromIntegral x)) primes)]
_
最初にリストに2があり、整数ごとにx
2より大きい場合は、すべてy
in primes
次のようにy <= sqrt(x)
、_x mod y != 0
_ =保持します。つまり、x
には、1とそれ自体以外の要素はありません。