我有一个与此类似的函数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
所以,有几个问题:
答案 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
。
您能解释一下更为惯用的解决方案的优势吗?我对函数式编程很陌生,有时我看不出我对循环的脏命令有什么问题。
除了主观之外,这是相当广泛的,并且在其他多个答案中得到的回答要比我在这里做得更彻底。