【问题标题】:How to make this code more idiomatic F#?如何使这段代码更符合 F# 的习惯?
【发布时间】:2012-07-23 09:08:43
【问题描述】:

我有一个与此类似的函数 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 循环命令。

【问题讨论】:

  • 这更适合CodeReview.SE。无论如何,fileCounter 声明在哪里,你为什么最终将unit 分配给files? (files 是打算成为一个函数吗?)
  • 抱歉,格式有误导性,并且缺少 fileCounter 的声明。我编辑了代码来解决这个问题。

标签: f# functional-programming


【解决方案1】:

这是一个更实用的例子:

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

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

【讨论】:

  • 谢谢。我修复了有点误导的代码并错过了声明:你能更新你的答案以反映它吗? fileCounter(实际上是 numEntries)与 numFiles 不同。 numFiles 是目录中的文件总数,用于打印“done x out of numFiles”。 fileCounter(重命名为 numEntries)是在文件中找到的条目总数。
  • 接受您的答案后,您至少应该使其可编译。 ;-](提示——Directory.EnumerateFiles 不返回数组。)
【解决方案2】:

假设您最终追求的是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,_) -&gt; numEntries 替换为ignore


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

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

【讨论】:

  • 该函数实际上返回单元:“processFile”作为副作用完成所有有用的工作(将条目写入磁盘)。我的问题不是关于一般的函数式编程,而是关于这个小sn-p的代码。我知道一般的想法,但我试图获得具体的例子来说明这一点。当然,问为什么这 7 行代码比我的 9 行代码更好,这不是一个太宽泛的问题。
  • @Dr_Asik :如果您希望函数返回unit,则将fun (_,numEntries,_) -&gt; numEntries 替换为ignore。即使询问这个特定代码与您发布的代码并不太宽泛,它肯定是主观的,根据定义,它不适合 SO。无论如何,就像我说的那样,SO 回答你的问题有几十个答案,更不用说在线上成千上万的其他文章了。
猜你喜欢
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2015-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多