F#seq行为

F#seq行为,f#,sequence,behavior,F#,Sequence,Behavior,我对F#中序列表达式的内部工作有点困惑 通常情况下,如果我们使用seq创建一个顺序文件读取器,而没有有意缓存数据 seq { let mutable current = file.Read() while current <> -1 do yield current } seq{ 让mutable current=file.Read() 而目前-1做 屈服电流 } 如果我们尝试重新迭代或回溯,最终会出现一些奇怪的行为

我对F#中序列表达式的内部工作有点困惑

通常情况下,如果我们使用seq创建一个顺序文件读取器,而没有有意缓存数据

 seq { 
       let mutable current = file.Read()
       while current <> -1 do
           yield current
     }
seq{
让mutable current=file.Read()
而目前-1做
屈服电流
}
如果我们尝试重新迭代或回溯,最终会出现一些奇怪的行为,我的想法是,因为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" 
让读取路径=
序号{
使用fp=System.IO.File.OpenRead路径
设buf=[| for uu0..1024->0uy |]
设可变位置=1
设可变电流=0
而0号位置是
如果电流=0,则
0号位置和当前位置<然后
产量单位[当前]

当前你的问题有点不清楚,所以我试着猜一下

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

请记住,
seq{}
生成要创建的
IEnumerable
。实际提供值的是
IEnumerator
。您可以用更经典的术语将其视为具有一个可以迭代的数组(iterable或enumerable)以及该数组上的许多指针,每个指针位于数组中的不同点(许多迭代器或枚举器)

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

但是,在第二个代码中,文件是作为
seq
块定义的一部分打开的。这意味着您每次迭代序列时都会得到一个新的文件句柄,或者,本质上,每个枚举器都有一个新的文件句柄。此代码之所以有效,是因为您不能反转枚举器或多次迭代它,而不是使用s至少有一根线


(现在,如果您手动获取一个枚举数并将其推进多个线程,您可能会很快遇到问题。但这是另一个主题。)

您如何重新迭代或回溯
seq
FileStream
已被缓冲。从:FileStream缓冲输入和输出以获得更好的性能。如果您有一个未缓冲的流,您可以将其打包,它将被缓冲。但您能解释一下您在这里尝试做什么吗?我们将以一些奇怪的行为结束如果我们尝试重新迭代或回溯,ior会令人困惑,因为序列不能被随机重绕或访问。@Sebastian,示例让s=读取“某个路径”;让s_1024=Seq.skip 1024 s;让s_1025=Seq.tail s_1024上述内容适用于同一序列的不同实例,它们也将属于不同的缓冲区。如下所述,我看到一些“奇怪”的原因获取相同值的原因是filestream的作用域。我正在实现一个“持久”文件读取器,它可以作为一个序列轻松地传递。它的使用将读取到某个点,然后如上面所示进行回溯,但gab稍大。具有持久含义,假设没有其他线程写入同时,我们将在序列的同一位置获得相同的输出。谢谢,可能是这样。我没有考虑创建filestream的作用域。我问这个问题的全部原因只是为了确保我在测试时涵盖了所有可能的角落情况,我可以看到我没有。对于多线程部分,这s不是一个问题,因为它不太可能从多个踏板中受益,而对其有害。