如何使这个代码更加惯用F#?

时间:2012-07-25 00:38:43

标签: f# functional-programming

我有一个与此类似的函数A,它将函数B应用于目录中的每个文件。每个文件都有一定数量的“条目”;函数B将当前的条目总数作为参数,并返回当前文件中找到的新条目的数量。

此外,我需要计算处理的文件数,并在每次处理文件时显示此计数。由于我的必要背景,我提出了2个可变变量和一个for循环。

let files = Directory.EnumerateFiles sourceDirectory
let mutable numEntries = 0
let mutable numFiles = Seq.length files
let mutable index = 0
for file in files do
     printfn "done %d of %d" index numFiles
     let numNewEntries = processFile file numEntries
     numEntries <- numEntries + numNewEntries
     index <- index + 1

所以,有几个问题:

  • 我怎么能用更惯用的功能性风格来写这个?
  • 您能解释一下更为惯用的解决方案的优势吗?我非常 功能编程的新手,有时我看不出有什么问题 我的肮脏的循环要求。

2 个答案:

答案 0 :(得分:6)

这是一个更实用的例子:

let files = Directory.EnumerateFiles sourceDirectory
let numFiles = Seq.length files
files 
|> Seq.mapi (fun idx file -> (idx,file)) // Get access to the index in a loop
|> Seq.fold (fun numentries (index,file) ->
         printfn "done %d of %d" index numFiles
         numentries + (processFile file numFiles)
         ) 0

通过使用mapi,我能够访问循环中的索引,从而消除了第一个可变变量。通过使用fold跟踪文件总数而不是可变变量来消除第二个。

这样做的主要优点是,没有任何可变状态,就可以更容易地将代码转换为在多个线程中运行。此外,由于变量是常量,因此对代码进行推理变得更加简单。

答案 1 :(得分:1)

假设你最终追求的是numEntries的最终价值,那么这就是我的看法:

let getNumEntries sourceDirectory =
    Directory.GetFiles sourceDirectory
    |> fun files -> (0, 0, files.Length), files
    ||> Array.fold (fun (index, numEntries, numFiles) file ->
        printfn "done %d of %d" index numFiles
        index + 1, numEntries + processFile file numEntries, numFiles)
    |> fun (_,numEntries,_) -> numEntries

如果你所追求的只是processFile而不是最终numEntries值的副作用,那么将fun (_,numEntries,_) -> numEntries替换为ignore


  

您能解释一下更为惯用的解决方案的优势吗?我对函数式编程很陌生,有时我看不出我对循环的脏命令有什么问题。

除了主观之外,这是相当广泛的,并且在其他多个答案中得到的回答要比我在这里做得更彻底。