为什么这个函数尾递归?

时间:2015-08-19 09:56:04

标签: f# tail-recursion

我写了一个基本的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:

enter image description here

1 个答案:

答案 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可能是一个有趣的练习,所以你可以看到素数一个接一个弹出......可能会让等待时间更加愉快