【问题标题】:Seq.unfold explanation in F#F# 中的 Seq.unfold 解释
【发布时间】:2010-10-06 17:25:36
【问题描述】:

我正在尝试使用 F# 懒惰地创建一个序列。

序列定义如下:

序列的第n项 三角形数由下式给出,tn = ½n(n+1);所以前十个三角形 数字是:

1、3、6、10、15、21、28、36、45、55、 ...

这是我目前所拥有的,但它似乎不起作用:

let tri_seq = 1.0 |> Seq.unfold (fun x -> match x with                                         
                                          | _ -> Some (x, 0.5*x*(x + 1.0)))

非常感谢谁能帮我弄清楚展开的工作原理。谢谢

编辑:我将第一个答案标记为正确,但它不起作用,但是我稍微修改它并且它起作用了。

let tri_seq = 1.0 |> Seq.unfold (fun x -> Some (0.5 * x * (x + 1.0),x + 1.0))

【问题讨论】:

    标签: f# seq.unfold


    【解决方案1】:

    首先,如果您只有一个案例,为什么要使用match

    let tri_seq = 1.0 |> Seq.unfold (fun x -> Some (x, 0.5 * x * (x + 1.0)))
    

    其次,什么“似乎不起作用”?你知道你产生了一个无限列表吗?

    /编辑:为了完整起见,这是正确的解决方案,OP 自己找到并发布为评论:

    let tri_seq = 
        1.0 |> Seq.unfold (fun x -> Some (0.5 * x * (x + 1.0), x + 1.0))
    

    【讨论】:

    • 我试过了,但是序列只返回一个.. > tri_seq;;验证它: seq = seq [1.0; 1.0; 1.0; 1.0; ...]
    • let tri_seq = 1.0 |> Seq.unfold (fun x -> Some (0.5 * x * (x + 1.0),x + 1.0)) 是我需要的。非常感谢。
    【解决方案2】:

    Brian 发布的代码的另一种替代方法是使用递归而不是命令式“while”循环:

    let tri = 
      let rec loop(n, diff) = seq { 
        yield n        
        yield! loop(n + diff, diff + 1.0) }
      loop(1.0, 2.0)
    printfn "%A" (tri |> Seq.take 10 |> Seq.to_list)
    

    它的效率要低得多(所以你必须在这里小心一点......),但它是更惯用的功能解决方案,因此可能更容易看到代码的作用。

    【讨论】:

    • 谢谢,但我不知道“Seq { }”是做什么的,而不是“yield”关键字,更不用说“!”了。如果您能花一些时间解释一下,将不胜感激!
    • 不是专家,但 Seq {} 的意思是让我从中得到一个序列。 yield 与 C# 相同 - 表示发回值并等待调用代码请求下一个。屈服!是(我相信)像 Seq.concat - 展平从递归调用返回的 seq 并对每个元素进行屈服。
    • 可以稍微努力使loop 至少是尾递归的,否则这不会在任何地方扩展
    • @pqnet F# 编译器实际上可以很好地处理递归 seq 表达式,所以这种模式工作得很好(它不像 C# 中的 foreach(x in y) yield x 那样会有问题)
    • @TomasPetricek 你是对的,这个函数实际上是一个尾调用,因为序列是惰性的,loop 返回一个序列,然后作为当前序列的延续返回。如果yieldyield! 的顺序相反,那会出现问题,但是这样可以优化
    【解决方案3】:

    这是另一种选择:

    let tri = seq {
        let n = ref 1.0
        let diff = ref 2.0
        while true do
            yield !n
            n := !n + !diff
            diff := !diff + 1.0
        }
    
    printfn "%A" (tri |> Seq.take 10 |> Seq.to_list)
    

    【讨论】:

      【解决方案4】:

      我知道这是一个很老的问题,但是当您确定 x * (x + 1) 是偶数并且确实可以被 2 整除时,我无法弄清楚为什么要使用浮点数。 所以我会简单地使用这个(我知道没有太大区别,但至少你有一个 int seq):

      let tri_seq = 1 |> Seq.unfold (fun x -> Some (x * (x + 1) / 2 , x + 1)) 
      
      tri_seq |> Seq.take 6 |> Seq.toList //[1; 3; 6; 10; 15; 21]
      

      fiddle

      (当然,除非您处理大量数字......)

      【讨论】:

        猜你喜欢
        • 2020-09-11
        • 1970-01-01
        • 2021-01-02
        • 2017-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多