何がうまくいかないのかについての良い説明についてはドリューの答えを読んでいますが、これを機能させる方法の簡単なデモンストレーションについては、
nextPrime xs = xs ++ [lastVal + nextCounts]
where
lastVal = (head . reverse) $ xs
isNextPrime y = 0 `elem` ( map ( y `mod`) xs )
-- ^ Style note, this name is super misleading, since it returns
-- false when the number is prime :)
nextVals = (map isNextPrime [lastVal, lastVal+1 ..] )
nextCounts = length $ takeWhile (\x -> x) nextVals
allPrimes xs = last np : allPrimes np
where np = nextPrime xs
これで、リストを作成しながら進みます。haskell は遅延しているため、 をnp
評価する前にの最後の要素を取得できallPrimes np
ます。つまり、無限ループではありませんhead (a : infiniteLoop)
。a
しかし、これは本当に非効率的です。Haskell ではリストは個別にリンクされているため、Python などとlast
はO(n)
対照的です。O(1)
また、最初のリストの長さについては++
コストがかかります。O(n)
その代わり
nextPrime xs = lastVal + nextCounts
where lastVal = head xs
isNextPrime = 0 `elem` map (y `rem`) xs
nextVals = map isNextPrime [lastVal ..]
nextCount = length $ takeWhile id nextVals
allPrimes xs = p : allPrimes (p:xs)
where p = nextPrime xs
そのため、コストのかかるトラバーサルを避けるために、リストを逆にしておきます。単純化することもできますnextPrime
import Data.Maybe
nextPrime xs = fromJust nextPrime
where isPrime y = not $ 0 `elem` map (rem y) xs
nextPrime = find isPrime [head xs ..]
素数である最初の要素のリストを検索し、それをリストに追加するだけです。次のfromJust
素数がない場合、エラーが発生します。しかし、常に次の素数があることが数学的にわかっているので、これは安全です。
最終的に、コードは次のようになります
import Data.Maybe
import Data.List
nextPrime xs = fromJust nextPrime
where isPrime y = 0 `notElem` map (rem y) xs
nextPrime = find isPrime [head xs ..]
allPrimes xs = p : allPrimes (p:xs)
where p = nextPrime xs
それを評価するには、 を呼び出しますallPrimes [2]
。
isPrime
これを行うためのさらにクリーンな方法は、数値が素数かどうかを返す関数を使用することです。そして、ただ持つために
allPrimes = filter isPrime [1..]
しかし、それは好奇心旺盛な読者に任せます。