持つ
primes = [p1, p2, p3, ..., pn, ...]
計算する
isPrime' n = foldr (\x acc -> (n `rem` x) /= 0 && acc) True primes
計算しているかのように
isPrime' n = rem n p1 /= 0 && (
rem n p2 /= 0 && (
rem n p3 /= 0 && (
..........
rem n pk /= 0 && (
.......... ..... ))))
複合n
s の場合、これは機能しrem
ます。式の 1 つが 0 になり、不等式が false になり、式全体も false になり&&
ます。
素数n
s の場合、これは問題を引き起こします:素数の中の素数自体rem
に到達するまで、素数の定義により、どの式も 0 になりません。p_i == n
しかし、まだ素数であることが検出されていないため、まだそこにはありません。ちょうど今それを行っているところです。それが素数かどうかをテストするには、それが素数であることを知る必要があります。これは明らかに悪い状況です。
primes
まだ存在しない値が要求されます。これは「ブラックホール」として知られています。エラーが発生し、計算が中止されます (またはスタックします)。
別の言い方をすれば、あたかも定義のように
primes = 2 : filter isPrime' [3,5..]
として定義されました
primes = 2 : [ n | n <- [3,5..]
, and [rem n p /= 0 | p <- takeWhile (< n) primes]]
n
問題は、が素数のときに停止するには、 in以上takeWhile (< n)
の素数に到達する必要があることですが、まだ到達していません。「ブラックホール」。p
n
primes
有名な「ふるい」コードは、ワークフローを「転置」することでこの問題を回避します。
primes = map head . iterate (\(x:xs)-> filter ((/=0).(`rem`x)) xs) $ [2..]
したがって、計算の非効率性(コードのより小さな問題)を保持しながら機能させ、不必要に多くの素数で候補をテストします(各素数は、その平方根を超えていないものだけではなく、先行するすべての素数によってテストされます)。不必要に遅い。
これは、テストを適切に早期に停止することで軽減されます。
primes = 2 : [ n | n <- [3,5..]
, and [rem n p /= 0 | p <- takeWhile ((<= n).(^2)) primes]]
これは、あなたの定義の観点から言えば、
isPrime' n = p1*p1 > n || rem n p1 /= 0 && (
p2*p2 > n || rem n p2 /= 0 && (
p3*p3 > n || rem n p3 /= 0 && (
..........
pk*pk > n || rem n pk /= 0 && (
.......... ..... ))))
これをベースの定義として書き留めることができますfoldr
(これは別の回答で見ることができます ここ )。