2012-11-21 12 views
8

Ich habe seit Ewigkeiten googeln und kann immer noch nicht die Antwort finden. Von dem, was ich F # 3.0 runnning auf .NET 4.5 nicht Endrekursion für eine rekursive Methode verwenden, wenn der Aufrufer den Anruf in einer try/catch gewickelt hat und/oder try/finally-Block zu verstehen. Wie ist die Situation, wenn es einen Versuch/Fang gibt oder versuchsweise/schließlich ein paar Stufen höher ist?Endrekursion und Ausnahmen in F #

+0

Was passiert, wenn Sie eine solche Funktion ausführen? –

Antwort

14

Wenn Sie den Körper von einigen (Schwanz) rekursive Funktion in einem try wickeln ... with Block dann ist die Funktion rekursiv nicht mehr Schwanz, weil der Anruf Rahmen nicht während der rekursiven Aufruf verworfen werden kann - es muss in bleiben der Stapel mit einem registrierten Ausnahmebehandler.

Zum Beispiel, sagen Sie so etwas wie iter Funktion für List haben:

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

Wenn Sie iter f [1;2;3] dann nennen es 4 verschachtelt Stack-Frames mit Exception-Handler erstellen wird (und wenn Sie rethrow in die with Zweig hinzugefügt, dann würde es tatsächlich die Fehlermeldung 4 mal drucken).

Sie können nicht wirklich Exception-Handler, ohne zu brechen Schwanz-Rekursion hinzufügen. Normalerweise benötigen Sie jedoch verschachtelte Ausnahmehandler nicht. So ist die beste Lösung ist es, die Funktion neu zu schreiben, so dass es keine Ausnahmen in jedem rekursiven Aufruf behandeln muss:

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 

Das hat etwas andere Bedeutung - aber es erzeugt keine verschachtelte Handler Ausnahme und loop kann immer noch sein, vollständig tail rekursiv.

Eine andere Option wäre, Ausnahmebehandlung nur über den Körper außer der Tail-rekursive Aufruf hinzuzufügen. Realistisch gesehen, das einzige, was eine Ausnahme in diesem Beispiel werfen kann, ist der Aufruf von f;

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