tail递归/高效函数来计算列表项(不使用List.count / Seq.count)

时间:2012-04-25 15:54:21

标签: f# tail-recursion

我尝试做一个尾递归函数来计算列表的元素,遵循规则,使用了一个累加器,但当我像这样运行它时:

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

可以做得更好吗?

3 个答案:

答案 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}