Poniższy C# funkcja:Jaki jest cel dodatkowego ldnull i ogona. w F # realizacji vs C#?
T ResultOfFunc<T>(Func<T> f)
{
return f();
}
kompiluje dziwi to:
IL_0000: ldarg.1
IL_0001: callvirt 05 00 00 0A
IL_0006: ret
ale równoważnej F # Funkcja:
let resultOfFunc func = func()
gromadzi do tego:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: tail.
IL_0005: callvirt 04 00 00 0A
IL_000A: ret
(Obie są w trybie zwolnienia). Na początku jest dodatkowy nop, którego nie jestem zbyt ciekawy, ale interesujące są dodatkowe instrukcje: ldnull
i tail.
.
Domyślam (prawdopodobnie źle) jest to, że ldnull
jest konieczne w przypadku, gdy funkcja jest void
więc nadal zwraca coś (unit
), ale to nie wyjaśnia, co jest celem instrukcji tail.
. A co się dzieje, gdy funkcja przesuwa coś na stos, czy nie utknęła z dodatkowym pustym miejscem, które nie zostanie zerwane?
Podejrzewam, że w tym przypadku funkcja została przekształcona w wywołanie ogona - nowa funkcja uzyskuje dostęp do przestrzeni stosu starej. –
Należy pamiętać, że może to spowodować pesymalizację wydajności, zobacz to pytanie: [Wydajność kara, gdy Generic.List .Add to ostatnia instrukcja funkcji i optymalizacja tailcall jest włączona] (http://stackoverflow.com/q/28649422/636019) –
ildjarn