私が知る限り、この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