F# 为什么不是';这个函数不是递归的吗?
我编写了一个基本的Eratosthenes的筛选函数,最初以F# 为什么不是';这个函数不是递归的吗?,f#,tail-recursion,F#,Tail Recursion,我编写了一个基本的Eratosthenes的筛选函数,最初以head::(innerSieve tail)结尾,我注意到它不是递归的 然后我对它进行了如下修改,以使用累加器。此代码现在应该是尾部递归的: let Sieve n = let rec innerSieve primes numbers = match numbers with | [] -> primes | h :: t -> innerSi
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)快速增长。我错过什么了吗
编辑:以n=100M的速度运行的VS屏幕截图:
回答您的问题:函数是尾部递归的-但这并不意味着它具有神奇的内存效率
真正的问题是
列表
在内存中的处理/保存方式(它们变得非常大、非常快)
这就是列表的问题:如果列表太大(开销太大……),它们就不是真正的最优,所以通常的第一步是使用数组
是的,它在这里工作得很好(memroy wise):
当然,这也会运行很长时间……但在我的机器上,内存消耗是TCO,这只意味着调用堆栈不会弹出。为什么您认为内存使用与尾部调用有关?您使用过探查器吗?是的(请参阅问题补充以获取屏幕截图)。当n=100M时,内存使用量很快超过2GB,并在不久后出现OutOfMemoryException。快照功能无法终止,但它可以检测数百万个对象。如果我理解正确,使用TCO,内存中最多应该有一个数字列表?那么,仅100.000.000 Int32值就约380MB(仅来自您的[2..n])-现在添加您正在生成的所有中间列表…似乎是在添加…不-这只是意味着您没有为堆栈上的函数调用分配内容谢谢!我对
List
和Array
之间的巨大差异感到非常震惊;我认为整数的链接列表在最坏的情况下是2-3倍大作为等效数组,由于指针的成本。一定要记住。
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