【问题标题】:F# seq behaviorF# 序列行为
【发布时间】:2021-03-16 10:37:31
【问题描述】:

我对 F# 中序列表达式的内部工作感到有些困惑。

通常,如果我们使用 seq 制作一个顺序文件读取器,而不是有意缓存数据

 seq { 
       let mutable current = file.Read()
       while current <> -1 do
           yield current
     }

如果我们尝试做一些重复或回溯,我们最终会出现一些奇怪的行为,我的想法是,因为 Read() 是一个调用一些可变值的函数,我们不能期望输出是正确的如果我们重新迭代。但是,即使在边界读取上,这也表现得很好?

let Read path =
    seq {
        use fp = System.IO.File.OpenRead path
        let buf = [| for _ in 0 .. 1024 -> 0uy |]
        let mutable pos = 1
        let mutable current = 0
        while pos <> 0 do
            if current = 0 then
                pos <- fp.Read(buf, 0, 1024)
            if pos > 0 && current < pos then
                yield buf.[current]
                current <- (current + 1) % 1024 
   } 

 let content = Read "some path" 

我们显然使用相同的缓冲区来提高性能,但是假设我们读取 1025 字节,它将触发对缓冲区的更新,如果我们在仍然获得正确输出后尝试读取位置

【问题讨论】:

  • 你如何重新迭代或回溯seq
  • FileStream 已被缓冲。来自docsFileStream 缓冲输入和输出以获得更好的性能。如果你有一个没有缓冲的流,你可以将它包装在BufferedStream 中,它会是。但是你能解释一下你想在这里做什么吗? 如果我们尝试进行一些重新迭代或回溯,我们最终会出现一些奇怪的行为会令人困惑,因为序列不能倒带或随机访问。
  • @Sebastian, 例如 let s = Read "some path";让 s_1024 = Seq.skip 1024 秒; let s_1025 = Seq.tail s_1024 以上适用于同一序列的不同实例,它们也将属于不同的缓冲区。如下所述,我在获得相同值时看到一些“奇怪”的东西的原因是文件流的范围。我正在实现一个“持久”文件阅读器,它可以作为一个序列传递以方便。它的使用会读到某个点,然后如上所示回溯,但要大一点。
  • 具有持久的含义,假设没有其他线程同时写入文件,我们将在序列的相同位置得到相同的输出

标签: f# sequence behavior


【解决方案1】:

你的问题有点不清楚,所以我会尝试猜测。

当您创建seq { } 时,您实际上是在创建一个状态机,它只会在需要时运行。当您向它请求第一个元素时,它将从顶部开始并运行到您的第一个 yield 指令。然后,当您请求另一个值时,它将从该点运行到下一个 yield,依此类推。

请记住,seq { } 会生成 IEnumerable&lt;'T&gt;,这就像“执行计划”。每次您开始迭代序列时(例如通过调用Seq.head),都会在后台调用GetEnumerator,从而创建一个新的IEnumerator&lt;'T&gt;。实际提供值的是IEnumerator。您可以用更经典的术语将其视为具有一个可以迭代的数组(可迭代或 enumerable)以及该数组上的许多指针,每个指针都位于数组中的不同点(许多迭代器或枚举器)。

在您的第一个代码中,file 很可能在 seq 块之外。这意味着您正在读取的文件被纳入执行计划;无论您开始迭代序列多少次,您都将始终从同一个文件中读取。这显然会导致不可预知的行为。

但是,在您的第二个代码中,文件作为seq 块定义的一部分打开。这意味着每次迭代序列时都会获得一个新的文件句柄,或者本质上是每个枚举器 的一个新文件句柄。这段代码有效的原因是您不能反转一个枚举器或对其进行多次迭代,至少不能使用单个线程。

(现在,如果您要手动获取一个枚举器并将其推进多个线程,您可能很快就会遇到问题。但这是一个不同的话题。)

【讨论】:

  • 谢谢,是的,可能是这样。我没有考虑创建文件流的范围。我询问的全部原因只是为了确保我在测试时已经涵盖了所有可能的极端情况,我可以看到我没有。对于多线程部分,这不是问题,因为它不太可能以对它有害的方式从多线程中受益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 2010-11-22
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多