在F#中,如何在不重新评估seq的情况下获取seq的头/尾

Mol*_*daa 3 f#

我正在读一个文件,我想用第一行做一些事情,还有其他所有其他行

let lines = System.IO.File.ReadLines "filename.txt" |> Seq.map (fun r -> r.Trim())

let head = Seq.head lines
let tail = Seq.tail lines
Run Code Online (Sandbox Code Playgroud)

```

问题:tail由于TextReader关闭,调用失败.这意味着Seq评估两次:一次得到head一次得到tail.

我怎样才能获得firstLine和lastLines,同时保持Seq和不重新评估Seq?

签名可以是,例如:

let fn: ('a -> Seq<'a> -> b) -> Seq<'a> -> b
Run Code Online (Sandbox Code Playgroud)

Wes*_*ser 7

最简单的事情可能只是Seq.cache用来包装你的lines序列:

let lines =
  System.IO.File.ReadLines "filename.txt"
  |> Seq.map (fun r -> r.Trim())
  |> Seq.cache
Run Code Online (Sandbox Code Playgroud)

值得注意的是文档:

该结果序列将具有与输入序列相同的元素.结果可以多次枚举.输入序列最多只列举一次,并且只在必要时列举.当重复评估原始序列中的项目在计算上是昂贵的或者如果迭代序列导致用户不希望多次重复的副作用时,高速缓存序列通常是有用的.

  • 如果你知道你要将整个文件读入内存,为什么不使用`File.ReadAllLines`,它将整个内容读入一个数组? (10认同)

Aar*_*ach 6

我通常使用一个seq表达式,其中 的Stream作用域在表达式内。这将允许您在处理流之前完全枚举序列。我通常使用这样的函数:

let readLines file =
    seq {
        use stream = File.OpenText file
        while not stream.EndOfStream do
            yield stream.ReadLine().Trim()
    }
Run Code Online (Sandbox Code Playgroud)

然后你应该能够调用Seq.head并获取失败中的第​​一行,并Seq.last获取文件中的最后一行。我认为这将在技术上创建两个不同的枚举器。如果您只想读取文件一次,那么将序列具体化为列表或使用类似的函数Seq.cache将是您的最佳选择。