私が知る限り、collect
関数は実際にはテール再帰型です。最初のケースでは明らかにacc
が返されます。 2番目のケースでは、最初に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
あなたがいますリリースモードでコンパイルしますか?テールコールは、リリースモードでない限り最適化されません。 – mydogisbox