F# F中的文件转换#
我刚刚开始与F#合作,试图了解典型的IDOM以及有效的思维和工作方式 手头的任务是将制表符分隔的文件简单转换为逗号分隔的文件。典型的输入行如下所示:F# F中的文件转换#,f#,F#,我刚刚开始与F#合作,试图了解典型的IDOM以及有效的思维和工作方式 手头的任务是将制表符分隔的文件简单转换为逗号分隔的文件。典型的输入行如下所示: let line = "@ES# 01/31/2006 13:31:00 1303.00 1303.00 1302.00 1302.00 2514 0" 我从循环代码开始,如下所示: // inFile and outFile defined in preceding code not shown here for line in
let line = "@ES# 01/31/2006 13:31:00 1303.00 1303.00 1302.00 1302.00 2514 0"
我从循环代码开始,如下所示:
// inFile and outFile defined in preceding code not shown here
for line in File.ReadLines(inFile) do
let typicalArray = line.Split '\t'
let transformedLine = typicalArray |> String.concat ","
outFile.WriteLine(transformedLine)
然后,我用一个Regex.Replace()替换了split/concat对操作:
现在,我们终于用管道取代了循环:
File.ReadLines(inFile)
|> Seq.map (fun x -> Regex.Replace(x, "\t", ","))
|> Seq.iter (fun y -> outFile.WriteLine(y))
// other housekeeping code below here not shown
虽然所有版本都能工作,但最终版本在我看来是最直观的。这就是一个更有经验的F#程序员完成这项任务的方式吗 我认为这三个版本都是F#专家编写的非常好的惯用代码 我通常更喜欢使用内置的语言功能编写代码(如
for
循环和if
条件),如果它们能让我解决问题的话。这些都是命令式的,但我认为当API需要命令式代码(如outFile.WriteLine
)时,使用它们是一个好主意。正如你提到的,你从这个版本开始(我也会这么做)
使用高阶函数也很好——尽管我可能只有在我想编写数据转换并获得新的序列或行列表时才会这样做——如果您使用File.writeAllines
而不是一行一行地编写,这将非常方便。尽管如此,也可以通过简单地用序列表达式包装第二个版本来实现:
let transformed =
seq { for line in File.ReadLines(inFile) -> Regex.Replace(line, "\t",",") }
File.WriteAllLines(outFilePath, transformed)
我认为没有任何客观的理由选择其中一个版本。我个人的风格偏好是对使用
,并对表达式进行重构以排序(如果需要),但其他人可能会不同意。另一个注意事项是,如果您想写入正在读取的同一个文件,您需要记住Seq正在进行延迟求值
使用数组而不是Seq可以确保在需要写入文件时关闭该文件进行读取
这是有效的:
let lines =
file |> File.ReadAllLines
|> Array.map(fun line -> ..modify line..)
File.WriteAllLines(file, lines)
这不会(导致文件访问文件冲突)
(可能与另一个讨论重叠,中间变量有助于解决同一问题)我会像你那样做,我会在第三个版本的最后一行省去匿名函数,只需这样做:|>Seq.iter outFile.WriteLine
这是一个非常好的[而且回顾起来,很明显]简化。谢谢谢谢,托马斯-非常感谢。
let lines =
file |> File.ReadAllLines
|> Array.map(fun line -> ..modify line..)
File.WriteAllLines(file, lines)
let lines =
file |> File.ReadLines
|> Seq.map(fun line -> ..modify line..)
File.WriteAllLines(file, lines)