そこで、私は素数を遅延生成する方法に取り組んでいました。これらの 3 つの定義を思いつきました。これらはすべて同等の方法で機能します。つまり、新しい整数のそれぞれが先行するすべての素数の因数を持っているかどうかをチェックするだけです。
primes1 :: [Integer]
primes1 = mkPrimes id [2..]
where mkPrimes f (x:xs) =
if f (const True) x
then
let g h y = y `mod` x > 0 && h y in
x : mkPrimes (f . g) xs
else
mkPrimes f xs
primes2 :: [Integer]
primes2 = mkPrimes id (const True) [2..]
where mkPrimes f f_ (x:xs) =
if f_ x
then
let g h y = y `mod` x > 0 && h y in
x : mkPrimes (f . g) ( f $ g $ const True) xs
else
mkPrimes f f_ xs
primes3 :: [Integer]
primes3 = mkPrimes [] [2..]
where mkPrimes ps (x:xs) =
if all (\p -> x `mod` p > 0) ps
then
x : mkPrimes (ps ++ [x]) xs
else
mkPrimes ps xs
したがって、すべての整数の再計算を回避
し(これまでに見つけた素数の数の順序で作業が必要だと思いますprimes2
)、新しいプライム。primes1
f_ = f (const True)
非科学的なテスト (ghci で実行) から見ると、よりも高速に実行take 1000
されるようです。primes3
primes2
このことから教訓を得て、関数を配列の操作として表現できる場合、効率のために後者の方法で実装する必要があると仮定する必要がありますか、それともここで何か他のことが起こっているのでしょうか?