我正在学习 OCaml(请原谅我的风格),并正在尝试编写一个函数来生成一个素数列表,直到某个上限。我已经设法以几种不同的方式做到这一点,所有这些都有效,直到您将它们缩放到相对较高的上限。
如何更改这些(其中任何一个)以使递归不会填满堆栈?我认为我的 while 循环版本会实现这一点,但显然不是!
发电机
let primes max =
let isPrime p x =
let hasDivisor a = (x mod a = 0) in
not (List.exists hasDivisor p) in
let rec generate p test =
if test < max then
let nextTest = test + 2 in
if isPrime p test then generate (test :: p) nextTest
else generate p nextTest
else p in
generate [5; 3; 2] 7;;
这是我最成功的解决方案,因为它在运行时不会立即溢出堆栈primes 2000000;;
。但是它只是坐在那里消耗CPU;我只能假设它最终会完成!以下替代方案都存在堆栈溢出问题:
埃拉托色尼的递归筛选
let primes max =
let rec sieve found toTest =
let h = List.hd toTest
and t = List.tl toTest in
let newPrimes = h :: found
and doesntDivide x = (x mod h <> 0) in
let nonDivisors = List.filter doesntDivide t in
if nonDivisors = [] then newPrimes
else sieve newPrimes nonDivisors in
let rec range a b =
if a > b then []
else a :: range (a + 1) b in
let p = range 2 max in
sieve [] p;;
Eratosthenes v2 的递归筛选
let primes max =
let rec sieve toTest =
let h = List.hd toTest
and t = List.tl toTest in
let doesntDivide x = (x mod h <> 0) in
let nonDivisors = List.filter doesntDivide t in
if nonDivisors = [] then [h]
else (h :: sieve nonDivisors) in
let rec range a b =
if a > b then []
else a :: range (a + 1) b in
let p = range 2 max in
sieve p;;
埃拉托色尼环筛
let primes max =
let rec range a b =
if a > b then []
else a :: range (a + 1) b in
let tail = ref (range 2 max)
and p = ref [] in
while !tail <> [] do
let h = List.hd !tail
and t = List.tl !tail in
let doesntDivide x = (x mod h <> 0) in
let newTail = ref (List.filter doesntDivide t) in
tail := !newTail;
p := h :: !p
done;
!p;;