F#中的文件转换

时间:2013-06-30 20:27:32

标签: f#

我刚刚开始使用F#并试图了解典型的idoms和有效的思考和工作方式。

手头的任务是将制表符分隔文件简单转换为以逗号分隔的文件。典型的输入行将如下所示:

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操作对:

for line in File.ReadLines(inFile) do   
    let transformedLine = Regex.Replace(line, "\t",",")
    outFile.WriteLine(transformedLine)

现在,最后,用管道替换了循环:

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#程序员如何完成这项任务?

2 个答案:

答案 0 :(得分:11)

我认为这三个版本都是F#专家写的非常好的惯用代码。

如果他们让我解决我遇到的问题,我通常更喜欢使用内置语言功能(如for循环和if条件)编写代码。这些是必要的,但我认为当API需要命令性代码(如outFile.WriteLine)时使用它们是个好主意。正如你所提到的 - 你从这个版本开始(我也会这样做)。

使用高阶函数也很好 - 虽然我可能只在我想编写数据 transformation 并获得一个新的序列或行列表时才会这样做 - 如果你是这样的话会很方便使用File.WriteAllLines而不是逐行编写行。虽然,也可以通过简单地用序列表达式包装第二个版本来完成:

let transformed = 
    seq { for line in File.ReadLines(inFile) -> Regex.Replace(line, "\t",",") }
File.WriteAllLines(outFilePath, transformed) 

我认为没有任何客观理由可以选择其中一个版本。我个人的风格偏好是使用for并重构序列表达式(如果需要),但其他人可能不同意。

答案 1 :(得分:0)

请注意,如果要写入要读取的同一文件,则需要记住Seq正在执行惰性评估。

使用数组而不是Seq可以确保在需要写入时关闭文件以供读取。

这有效:

            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)

(可能与另一个讨论here重叠,其中中间变量有助于解决同一问题)