1

延迟评估对于处理无法一次性放入主内存的大文件是一个很大的好处。但是,假设序列中有一些我想立即评估的元素,而其余的可以懒惰地计算——有什么方法可以指定吗?

具体问题:(如果有助于回答问题)

具体来说,我使用一系列 IEnumerables 作为多个序列的迭代器——这些序列是从使用 BinaryReader 流打开的文件中读取的数据(每个序列负责从其中一个文件中读取数据)。这些上的 MoveNext() 将按特定顺序调用。例如。iter0然后iter1然后iter5....iter3等等。此顺序在另一个序列index=中指定{0,1,5,3,....}。然而序列是惰性的,评估自然只在需要时进行。因此,文件读取(对于从磁盘上文件读取的开头的序列)发生在序列的 IEnumerables 移动时。这导致非法文件访问 - 一个进程正在读取的文件再次被访问(根据错误消息)。

诚然,非法文件访问可能是出于其他原因,在尽我最大努力调试其他原因之后,可能值得一试。

4

2 回答 2

3

虽然我同意 Tomas 的评论:如果文件共享处理得当,你不应该需要这个,这里有一种方法可以急切地评估前N个元素:

let cacheFirst n (items: seq<_>) =
  seq {
    use e = items.GetEnumerator()
    let i = ref 0
    yield! 
      [ 
        while !i < n && e.MoveNext() do
          yield e.Current
          incr i
      ]
    while e.MoveNext() do
      yield e.Current
  }

例子

let items = Seq.initInfinite (fun i -> printfn "%d" i; i)

items
|> Seq.take 10
|> cacheFirst 5
|> Seq.take 3
|> Seq.toList

输出

0
1
2
3
4
val it : int list = [0; 1; 2]
于 2012-07-03T15:00:46.897 回答
1

丹尼尔的解决方案是合理的,但我认为我们不需要另一个操作员,只是Seq.cache在大多数情况下。

首先缓存您的序列:

let items = Seq.initInfinite (fun i -> printfn "%d" i; i) |> Seq.cache

渴望评估,然后从一开始就进行惰性访问:

let eager = items |> Seq.take 5 |> Seq.toList
let cached = items |> Seq.take 3 |> Seq.toList

这将评估前 5 个元素一次(在 期间eager),但将它们缓存以供辅助访问。

于 2012-07-04T01:45:01.897 回答