【问题标题】:Why does iterating previously read-in sequence trigger a new read?为什么迭代先前读入的序列会触发新的读入?
【发布时间】:2016-09-27 18:50:03
【问题描述】:

在这个 SO post 中,添加

 inSeq 
    |> Seq.length
    |> printfn "%d lines read"

导致 inSeq 中的惰性序列被读入。

好的,我已经扩展了该代码并想首先打印出该序列(请参阅下面的新程序)。

当 Visual Studio (2012) 调试器到达时

inSeq |> Seq.iter (fun x -> printfn "%A" x)

读取过程重新开始。当我使用调试器检查inSeq 时,inSeq 中似乎没有任何元素。

如果我首先将元素读入inSeq,我如何查看(检查)这些元素,为什么它们不会在调用Seq.iter 时打印出来?

open System
open System.Collections.Generic
open System.Text
open System.IO
#nowarn "40"


let rec readlines () =
    seq {
        let line = Console.ReadLine()
        if not (line.Equals("")) then
            yield line
            yield! readlines ()
}

[<EntryPoint>]
let main argv = 
    let inSeq = readlines ()

    inSeq 
    |> Seq.length
    |> printfn "%d lines read"

    inSeq |> Seq.iter (fun x -> printfn "%A" x)
    // This will keep it alive enough to read your output
    Console.ReadKey() |> ignore    
    0

我在某处读到延迟评估的结果没有被缓存。这就是这里发生的事情吗?如何缓存结果?

【问题讨论】:

  • 我将在哪里使用它以及为什么?
  • let inSeq = readlines () |&gt; Seq.cache
  • Seq 本质上是一个IEnumerable&lt;T&gt;。就像 C# 一样,每次迭代都会重新启动迭代器。当编译器或 Resharper 检测到 IEnumerable&lt;T&gt; 的多次迭代时,实际上会发出警告

标签: f# seq


【解决方案1】:

Sequence 不是物品的“容器”,而是在未来某个时间交付物品的“承诺”。您可以将其视为您调用的函数,但它以块的形式返回其结果,而不是一次全部返回。如果您调用该函数一次,它会返回一次结果。如果你第二次调用它,它会第二次返回结果。

因为您的特定序列不是纯的,您可以将其与非纯函数进行比较:您调用一次,它会返回一个结果;你第二次调用它,它可能会返回不同的东西。

序列在第一次读取后不会自动“记住”它们的项目 - 与函数在第一次调用后不会自动“记住”它们的结果完全相同。如果你想从一个函数中获得它,你可以将它包装在一个特殊的“缓存”包装器中。所以你也可以为一个序列做。

“缓存返回值”的通用技术通常称为“memoization”。特别是对于 F# 序列,它在 Seq.cache 函数中实现。

【讨论】:

  • 感谢您的回答。应该使用哪种原生 F# 数据结构来代替序列,以便能够添加到结构中并保留其内容以进行更多处理?
  • List 也许?真的要看具体的用法,不同的数据结构会有不同的性能和内存特性。
  • 谢谢。 @FyodorSoikin 我也在考虑列表。
  • 请注意:不要试图通过重复附加项目或类似的方式来构建列表。只需获取您的序列并在其上使用Seq.toList
  • 重复向列表追加项目会发生什么?
猜你喜欢
  • 2017-05-26
  • 2012-02-10
  • 2021-05-17
  • 1970-01-01
  • 2016-05-01
  • 1970-01-01
  • 2021-12-06
  • 2016-09-13
  • 2018-04-07
相关资源
最近更新 更多