konto usunięte
Temat: Tail call w C#, F#
Kiedyś w tym wątku wspomniałem, że zainteresuję się obsługą tail call i tym samym chciałem się podzielić moimi próbami.Najpierw przykład w F# - rekurencyjne wyliczenie sumy wyrazów ciągu arytmetycznego 1, 2 ... aż do 1 miliona! Ten milion gwarantuje, że bez użycia tailcall będziemy mieli StackOverflowException. I tak F#:
let rec SumIt aList acc:int64 =
match aList with
| [] -> acc
| head::rest -> SumIt rest (acc+head)
let sum = SumIt [1L..1000000L] 0L
printfn "Wynik: %d" sum
działa. Jak widać F# jako język funkcjonalny radzi sobie dobrze z rekurencją.
Oto mniej więcej odpowiednik w C# - różnice wynikają po prostu z różnych języków, ale koncepcyjnie algorytm ten sam: wywołaj rekurencyjnie sumę dla pozostałych elementów na liście:
static class Program {
const int MAX_LENGTH = 1000000;
static void Main(string[] args) {
int[] _init = new int[MAX_LENGTH];
for(int i=0; i<MAX_LENGTH; i++) _init[i] = i+1;
Console.WriteLine(SumIt(_init, 0, 0L));
}
static Int64 SumIt(IList<int> aList, int pos, Int64 acc) {
return (pos < MAX_LENGTH) ? SumIt(aList, pos+1, acc + aList[pos]) : acc;
}
}
No i tutaj mamy StackOverflowException - kompilator C# nie generuje - przynajmniej mi się nie udało go do tego zmusić - wywołania tail call. Natomiast mógłby, o czym łatwo się można przekonać wprowadzając poprawkę na poziomie IL. Wpierw dekompilacja:
ildasm ConsoleApplication1.exe /out:test.il
potem w notatniku trzeba dodać prefix tail.:
IL_0017: add
tail. //o tutaj, mniej wiecej pod koniec
IL_0018: call int64 ConsoleApplication1.Program::SumIt(class [mscorlib]System.Collections.... ciach
ponowna kompilacja:
ilasm test.il /out:test.exe
i nie ma już StackOverflowException.
Jak widać, gdyby kompilator C# chciał to mógłby... a sprawdzałem w .NET 3.5SP1, 4.0 i to samo. Coś mi mówi, że F# nieźle namiesza w głowach:)