0

我有一个与此类似的函数 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

所以,有几个问题:

  • 我怎样才能以更惯用、更实用的风格来写这个?
  • 你能解释一下更惯用的解决方案的优势吗?我对函数式编程非常陌生,有时我看不出我肮脏的 for 循环命令有什么问题。
4

2 回答 2

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跟踪文件总数而不是可变变量来消除的。

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

于 2012-07-25T01:05:17.533 回答
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,_) -> numEntriesignore.


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

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

于 2012-07-25T02:35:32.697 回答