在F#中不使用List <List <string >>获取列表列表

one*_*e-t 4 f#

我在这里有这个功能:

let ProcessFile (allLines: string list) = 
    let list = new List<List<string>>()

    let rec SplitFile (input: string list) =
        if input.Length <> 0 then
            list.Add(new List<string>(input.TakeWhile(fun x -> x <> "")))
            let nextGroup = input.SkipWhile(fun x -> x <> "").SkipWhile(fun x -> x = "")
            SplitFile (Seq.toList nextGroup)

    SplitFile allLines |> ignore
    list
Run Code Online (Sandbox Code Playgroud)

它将文件的内容作为字符串列表给出,并将每个由空行分隔的组作为单独的列表,给出一个列表列表.

我的问题是,有没有更好的方法来实现这一点,让我有一个像字符串列表一样的东西,而不是我必须使用新的List <List <string >>?因为这对我来说似乎并不特别.

Jon*_*rop 5

一个更惯用的解决方案可能是:

let processFile xs =
  let rec nonEmpties n = function
    | [] as xs | ""::xs -> n, xs
    | _::xs -> nonEmpties (n+1) xs
  let rec loop xs =
    seq { match xs with
          | [] -> ()
          | ""::xs -> yield! loop xs
          | xs ->
              let n, ys = nonEmpties 0 xs
              yield Seq.take n xs
              yield! loop ys }
  loop xs
Run Code Online (Sandbox Code Playgroud)

其中嵌套nonEmpties函数计算给定列表前面有多少非空元素,并返回最后一个非空元素后的count和tail列表,该loop函数跳过空元素并生成非空元素序列.

该解决方案的一些有趣特征:

  • 完全尾递归,因此它可以处理任意长的非空字符串序列和非空字符串序列序列.

  • 通过返回输入列表避免复制.

在测试输入1000个1000串的序列时,这个解决方案比yamen快8倍,比Tomas快50%.

这是一个更快的解决方案,首先将输入列表转换为数组,然后对数组索引起作用:

let processFile xs =
  let xs = Array.ofSeq xs
  let rec nonEmpties i =
    if i=xs.Length || xs.[i]="" then i else
      nonEmpties (i+1)
  let rec loop i =
    seq { if i < xs.Length then
            if xs.[i] = "" then
              yield! loop (i+1)
            else
              let j = nonEmpties i
              yield Array.sub xs i (j - i)
              yield! loop j }
  loop 0
Run Code Online (Sandbox Code Playgroud)

在测试输入1000个1000串的序列时,这个解决方案比yamen快34倍,比Tomas快6倍.