我写了一个基本的Eratosthenes的筛选函数,最初以head::(innerSieve tail)
结束,我注意到它不是递归的。
然后我按如下方式修改它以使用累加器。这段代码现在应该是尾递归的:
let Sieve n =
let rec innerSieve primes numbers =
match numbers with
| [] -> primes
| h :: t -> innerSieve (h :: primes) (List.filter (fun x -> x % h > 0) t)
innerSieve [] [2 .. n] |> List.rev
printf "%A" (Sieve 10000)
但是,即使在发布模式下,此功能的内存使用量也会以n
的大小(每+1000 + 1-2MB)极快地增长。我错过了什么吗?
编辑:VS的屏幕截图,运行n = 100M:
答案 0 :(得分:2)
对于你的问题:函数是 尾递归 - 这并不意味着它具有神奇的记忆效果。
你的真正的问题是List
处理/保存在内存中的方式(那些非常大,非常快。
这就是列表的问题:如果它们变大(开销很大......)它们并不是最佳的,所以通常的第一步是改为数组。
是的,它在这里运作良好(memroy-wise):
let inline divides d n = n % d > 0
let rec innerSieve primes numbers =
if Array.isEmpty numbers
then primes
else
let h = numbers.[0]
let numbers' = numbers |> Array.filter (divides h)
in innerSieve (h :: primes) numbers'
let Sieve n =
innerSieve [] [| 2 .. n |] |> List.rev
当然这也将很长时间 ......但是在我的机器上,内存消耗<200MB,所以IMO也不错(当然我的机器还在思考和会这么一段时间,所以我可能只是再次杀死这个过程并将其称为一天 - 你可以让它运行而不是;))。
顺便说一下:将它切换到seq
而不是list
可能是一个有趣的练习,所以你可以看到素数一个接一个弹出......可能会让等待时间更加愉快