我尝试做一个尾递归函数来计算列表的元素,遵循规则,使用了一个累加器,但当我像这样运行它时:
lstcountr [1..98765432];;
我明白了:
System.OutOfMemoryException:抛出了类型'System.OutOfMemoryException'的异常。
这是我的功能(我认为它是尾递归/高效):
let lstcountr ls =
let rec loop ls total =
match ls with
| [] -> total
| hd::tl -> loop tl total+1I
loop ls 0I
可以做得更好吗?
答案 0 :(得分:8)
你的函数不是尾递归。
| hd::tl -> loop tl total+1I
应该是
| hd::tl -> loop tl (total+1I)
操作符在函数调用之后被解释,通常在这种情况下你无法分辨,因为结果是相同的,但尾递归则不然。
同样如Tejs所说,您正在创建一个包含太多项目的列表,这些项目会导致您OutOfMemoryException
。您是否尝试过使用seq { }
?
答案 1 :(得分:5)
太多的递归意味着你得到StackOverflowException
,而不是OutOfMemoryException
- 这是因为你试图一次创建一个98765432元素的列表。
无论你的递归如何,这个列表都会在参数创建时在内存中传递,而不是懒惰我可能会添加。
答案 2 :(得分:2)
以下是两种编写基于序列的版本的方法,该版本的返回类型具有多态性:
module Seq =
open LanguagePrimitives
let inline count items = Seq.fold (fun i _ -> i + GenericOne) GenericZero items
//faster
let inline count (items: seq<_>) =
use e = items.GetEnumerator()
let rec loop n =
if e.MoveNext() then loop (n + GenericOne)
else n
loop GenericZero
这允许您使用最合适的类型计算长度:
let n : bigint = Seq.count {1I .. 987298234982374923847I}
let n : float = Seq.count {1I .. 987298234982374923847I}