据我所知,该collect
函数实际上是尾递归的。第一种情况显然只是返回acc
. 第二种情况首先调用FindSourceFilesForTarget
,然后调用Set.union
,然后返回。您可以将其重写如下(更清楚地显示尾递归):
| hr::tl ->
let sources = FindSourceFilesForTarget hr
let acc = Set.union acc sources
collect tl
因为这只是一个调用自身的单个函数,所以编译器将其优化为一个循环。这是编译后的代码的样子(当你使用反射器将其转换为 C# 时):
public static FSharpSet<int> collect(FSharpList<int> t, FSharpSet<int> acc) {
while (true) {
FSharpList<int> fSharpList = t;
if (fSharpList.TailOrNull == null) break;
// The following corresponds to the second case
FSharpList<int> tl = fSharpList.TailOrNull;
int hr = fSharpList.HeadOrDefault;
// Variables 'acc' and 't' are mutated (instead of calling the function)
acc = SetModule.Union<int>(acc, Program.FindSourceFilesForTarget<int>(hr));
t = tl;
}
return acc;
}
在稍微无关的注释中,您还可以使用标准库函数来表达这一点:
t |> Seq.map FindSourceFilesForTarget |> Set.unionMany