如何在函数式编程 (F#) 中将行号添加到文本文件?
How to add line numbers to a text file in functional programming (F#)?
它适用于 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
但这非常 "non-functional" 所以我很好奇执行此操作的正确方法是什么?
我想出了如何将索引号附加到字符串列表中:
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
对于 Seq
模块中的某些功能(与 List
相同,...)您会发现带有附加 i
的版本 - 例如 Seq.map
你会发现 Seq.mapi
这就是你要找的东西 - 除了你的 collection 的值之外,你还得到(作为第一个参数)索引:
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
已经 returns () : 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.zip
和 List.zip
在 [=42= 方面也有不同的行为]s 具有不同的 item-count - Seq.zip
在一个 collection 运行时停止尝试但是 List.zip
希望两个列表具有相同的大小并且如果不是 [=28 将抛出异常=]
您的 addIndex
函数实际上是正确的 - 但它适用于 F# 列表。 ReadLine
函数 returns IEnumerable<T>
而不是 F# 列表(这是有道理的,因为它是一个 .NET 库)。您可以通过添加 List.ofSeq
来修复 addLnNum2
函数(将 IEnumerable<T>
转换为列表):
let addLnNum2 filename =
let added =
File.ReadLines(filename)
|> List.ofSeq
|> addIndex 1
File.WriteAllLines("out.txt", added)
使用 Carsten 的回答中提到的 Seq.mapi
或 Seq.zip
当然比实现您自己的递归函数更简单,但您确实正确地实现了递归和模式匹配:-)。
它适用于 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
但这非常 "non-functional" 所以我很好奇执行此操作的正确方法是什么? 我想出了如何将索引号附加到字符串列表中:
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
对于 Seq
模块中的某些功能(与 List
相同,...)您会发现带有附加 i
的版本 - 例如 Seq.map
你会发现 Seq.mapi
这就是你要找的东西 - 除了你的 collection 的值之外,你还得到(作为第一个参数)索引:
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
已经 returns () : 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.zip
和 List.zip
在 [=42= 方面也有不同的行为]s 具有不同的 item-count - Seq.zip
在一个 collection 运行时停止尝试但是 List.zip
希望两个列表具有相同的大小并且如果不是 [=28 将抛出异常=]
您的 addIndex
函数实际上是正确的 - 但它适用于 F# 列表。 ReadLine
函数 returns IEnumerable<T>
而不是 F# 列表(这是有道理的,因为它是一个 .NET 库)。您可以通过添加 List.ofSeq
来修复 addLnNum2
函数(将 IEnumerable<T>
转换为列表):
let addLnNum2 filename =
let added =
File.ReadLines(filename)
|> List.ofSeq
|> addIndex 1
File.WriteAllLines("out.txt", added)
使用 Carsten 的回答中提到的 Seq.mapi
或 Seq.zip
当然比实现您自己的递归函数更简单,但您确实正确地实现了递归和模式匹配:-)。