这个惯用的F#是一个相当快速的无限递归序列吗?

Nic*_*ckL 5 f# immutability seq

在解决第12个项目的欧拉问题时,我开始着手制作无限序列以获得连续的三角形数字.我的第一次尝试是:

let slowTriangularNumbers =

  let rec triangularNumbersFrom n = 
    seq { 
      yield seq { 0 .. n } |> Seq.sum
      yield! triangularNumbersFrom (n + 1) 
    }

   triangularNumbersFrom 1
Run Code Online (Sandbox Code Playgroud)

事实证明这是非常缓慢的 - 每次获得下一个项目时,它必须计算导致的所有增加n.

我的下一次尝试是:

let fasterTriangularNumbers =

  let cache = System.Collections.Generic.Dictionary<int, int>()
  cache.[0] <- 0         

  let rec triangularNumbersFrom n = 
  seq { 
    cache.[n] <- cache.[n - 1] + n                   
    yield cache.[n]
    yield! triangularNumbersFrom (n + 1) 
  }

  triangularNumbersFrom 1
Run Code Online (Sandbox Code Playgroud)

引入可变字典已经大大加快了它的速度,但是有一个可变集合这是正常的,还是我可以用另一种方式表示状态?

Jon*_*rop 8

我认为这更惯用:

Seq.unfold (fun (i, n) -> Some(n, (i+1, n+i))) (2, 1)
Run Code Online (Sandbox Code Playgroud)

您可能更喜欢这种选择:

seq { 2 .. System.Int32.MaxValue }
|> Seq.scan (+) 1
Run Code Online (Sandbox Code Playgroud)

  • @NickL`Seq.unfold`迭代地调用指定的函数(但是懒得!),直到它返回`None`.通过总是返回`Some`,Jon的实现产生了无限的序列. (2认同)
  • @NickL:手工编写序列表达式(除了简洁性)的主要优点是确保没有尾调用问题.您可以轻松编写泄漏堆栈空间的序列表达式,从而导致堆栈溢出.如果使用`Seq.unfold`,则该问题不存在. (2认同)