【问题标题】:Is this idiomatic F# for a fairly quick infinite recursive sequence?这是一个相当快速的无限递归序列的惯用 F# 吗?
【发布时间】:2013-12-27 18:18:21
【问题描述】:

在解决the 12th project euler problem 的过程中,我着手制作一个无限序列来获得连续的三角数。我的第一次尝试是:

let slowTriangularNumbers =

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

   triangularNumbersFrom 1

结果证明这非常慢 - 每次获取下一项时,它都必须计算导致 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

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

【问题讨论】:

    标签: f# immutability seq


    【解决方案1】:

    我认为这更惯用:

    Seq.unfold (fun (i, n) -> Some(n, (i+1, n+i))) (2, 1)
    

    您可能更喜欢这种选择:

    seq { 2 .. System.Int32.MaxValue }
    |> Seq.scan (+) 1
    

    【讨论】:

    • 我猜这相当于: let rec loop(n, diff) = seq { yield n yield! loop(n + diff, diff + 1) } loop(1, 2) 由 Tomas Petricek 在另一个答案中提供。你介意解释一下“Some”在这里做什么吗?
    • @NickL Seq.unfold 反复调用指定的函数(但很懒惰!),直到它返回 None。通过始终返回Some,Jon 的实现产生了一个无限序列。
    • @NickL:与手工编写序列表达式相比(除了简洁之外)的主要优点是确保没有尾调用问题。您可以轻松编写泄漏堆栈空间导致堆栈溢出的序列表达式。如果你使用Seq.unfold,这个问题就不存在了。
    猜你喜欢
    • 2016-04-25
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 2021-03-25
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多