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

时间:2018-08-17 17:02:21

标签: 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

```

问题:由于tail已关闭,因此对TextReader的调用失败。 这意味着对Seq进行两次评估:一次获取head,一次获取tail

如何在保持Seq且不重新评估Seq的情况下获得firstLine和lastLines?

签名可以是,例如:

let fn: ('a -> Seq<'a> -> b) -> Seq<'a> -> b

2 个答案:

答案 0 :(得分:8)

最简单的操作可能只是使用Seq.cache来包装lines序列:

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

文档中的注释:

  

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

答案 1 :(得分:4)

我通常使用seq表达式,其中Stream的作用范围是表达式内部。这样一来,您就可以在处理流之前完全枚举序列。我通常使用这样的功能:

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

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