【问题标题】:How to add line numbers to a text file in functional programming (F#)?如何在函数式编程(F#)中将行号添加到文本文件?
【发布时间】:2015-12-19 03:50:26
【问题描述】:

它适用于 for 循环和可变变量:

let addLnNum filename =    
    use outFile = new StreamWriter(@"out.txt")    
    let mutable count = 1
    for line in File.ReadLines(filename) do
        let newLine = addPre (count.ToString()) line
        outFile.WriteLine newLine
        count <- count + 1

但它非常“不起作用”,所以我很好奇这样做的正确方法是什么? 我想出了如何将索引号附加到字符串列表:

let rec addIndex (startInd:int) l=
    match l with
    |x::xs ->  startInd.ToString()+x :: (addIndex (startInd+1) xs)
    |[] -> []

但它不适用于 File.ReadLines:

let addLnNum2 filename =    
    use outFile = new StreamWriter(@"out.txt")    
    File.ReadLines(filename)
    |> addIndex 1
    |> ignore
    //Error 1   Type mismatch. Expecting a Collections.Generic.IEnumerable<string> -> 'a    
    //but given a string list -> string list    

将整个文件作为列表读入内存是唯一的方法吗?有没有类似 seq.count 的东西,所以可以像下面这样完成?

let addLnNum3 filename =    
    use outFile = new StreamWriter(@"out.txt")    
    File.ReadLines(filename)
    |> Seq.map (fun s -> Seq.count + s) //no such thing as Seq.count
    |> Seq.iter outFile.WriteLine 
    |> ignore

【问题讨论】:

    标签: f# seq readlines


    【解决方案1】:

    对于Seq 模块中的某些函数(与List 相同,...),您会发现带有附加i 的版本 - 例如对于Seq.map,您会发现Seq.mapi 和这个是您正在寻找的 - 除了从您的集合中获得的值(作为第一个参数)之外,您还获得了索引:

    let addLnNums filename =    
        use outFile = new System.IO.StreamWriter (@"out.txt")
        System.IO.File.ReadLines filename
        |> Seq.mapi (sprintf "%d: %s")
        |> Seq.iter outFile.WriteLine
    

    还请注意,您不需要ignore,因为Seq.iter 已经返回() : unit

    如果我们没有这个,那么函数式方法就是像这样使用Zip

    let addLnNum filename =    
        use outFile = new System.IO.StreamWriter (@"out.txt")
        Seq.zip (Seq.initInfinite id) (System.IO.File.ReadLines filename)
        |> Seq.map (fun (index, line) -> sprintf "%d: %s" index line)
        |> Seq.iter outFile.WriteLine
    

    其中(除了将函数取消到map之外)基本相同


    注意:

    对于您显然没有List.initInfinte 的列表,所以只需使用Seq - 同样Seq.zipList.zip 对于具有不同项目计数的集合有不同的行为 - Seq.zip 在一个集合时停止运行 try 但 List.zip 希望两个列表大小相同,否则会抛出异常

    【讨论】:

    • 谢谢!!非常感谢 Zip 方法的解决方案,因为我正在尝试学习如何以函数式风格做事
    【解决方案2】:

    您的 addIndex 函数实际上是正确的 - 但它适用于 F# 列表。 ReadLine 函数返回 IEnumerable&lt;T&gt; 而不是 F# 列表(这是有道理的,因为它是一个 .NET 库)。您可以通过添加List.ofSeq 来修复addLnNum2 函数(将IEnumerable&lt;T&gt; 转换为列表):

    let addLnNum2 filename =    
        let added = 
          File.ReadLines(filename)
          |> List.ofSeq
          |> addIndex 1
        File.WriteAllLines("out.txt", added)
    

    使用 Carsten 的回答中提到的 Seq.mapiSeq.zip 肯定比实现自己的递归函数更简单,但你确实得到了正确的递归和模式匹配:-)。

    【讨论】:

    • 请注意,List.ofSeq 将创建内存中所有内容的完整列表(只是评论,因为它在问题中提到)
    猜你喜欢
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    • 2021-12-30
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    相关资源
    最近更新 更多