2012-11-21 20 views
8

Przeszukując od dawna, wciąż nie mogę znaleźć odpowiedzi. Z tego, co rozumiem, podczas uruchamiania F # 3.0 w .NET 4.5 nie będzie używana rekursja tylna dla metody rekursywnej, jeśli wywoływacz zawija połączenie w try/catch i/lub try/finally block. Jaka jest sytuacja, jeśli istnieje próba/catch lub próba/w końcu kilka poziomów na stosie?Rekurencja ogona i wyjątki w F #

+0

Co się stanie, jeśli uruchomisz taką funkcję? –

Odpowiedz

14

Jeśli owinąć ciało jakiegoś (ogon) funkcji rekurencyjnej w try ... with blok to funkcja nie jest już ogon rekurencyjne, ponieważ rama połączenie nie może zostać odrzucone podczas rekurencyjnego wywołania - musi pozostać w stos z zarejestrowaną obsługą wyjątku.

Na przykład, powiedzmy, że masz coś podobnego iter funkcji dla List:

let rec iter f list = 
    try 
    match list with 
    | [] ->() 
    | x::xs -> f x; iter f xs 
    with e -> 
    printfn "Failed: %s" e.Message 

Po wywołaniu iter f [1;2;3] następnie stworzy 4 zagnieżdżonych ramek stosu z obsługą wyjątków (a jeśli dodano rethrow do with oddziału, następnie wydrukowałby komunikat o błędzie 4 razy).

Naprawdę nie można dodawać procedur obsługi wyjątków bez łamania rekurencji ogona. Jednak zazwyczaj nie potrzebujesz zagnieżdżonych procedur obsługi wyjątków. Tak więc najlepszym rozwiązaniem jest przepisać funkcję tak, że nie trzeba obsłużyć wyjątki w każdym rekurencyjnego wywołania:

let iter f list = 
    let rec loop list = 
    match list with 
    | [] ->() 
    | x::xs -> f x; loop xs 
    try loop list 
    with e -> printfn "Failed: %s" e.Message 

To ma trochę inne znaczenie - ale nie tworzyć zagnieżdżone obsługi wyjątków i loop może być nadal w pełni rekurencyjny.

Inną opcją byłoby dodanie obsługi wyjątku tylko w stosunku do obiektu z wyłączeniem wywołania rekurencyjnego. Realistycznie, jedyną rzeczą, która może rzucić wyjątek w tym przykładzie jest połączenie z f;

let rec iter f list = 
    match list with 
    | [] ->() 
    | x::xs -> 
    try 
     f x 
    with e -> 
     printfn "Failed: %s" e.Message 
    iter f xs