2013-07-09 5 views
7

ich die Erlang LektionKein tailrekursiver Code in einem try catch-Block?

bei http://learnyousomeerlang.com/errors-and-exceptions Ich verstehe nicht, diesen Teil liest:

Die Expression zwischen Versuch und soll geschützt werden. Dies bedeutet, dass jede Art von Ausnahme innerhalb dieses Anrufs abgefangen wird.

Und

der geschützten Teil einer Ausnahme kann nicht Schwanz rekursiv sein.

[...]

Durch Ihre rekursive Aufrufe zwischen der von und zu fangen setzen, sind Sie nicht in einem geschützten Teil, und Sie werden von Last Call-Optimierung profitieren.

Also können wir keine rekursiven Aufrufe in dem Teil, wo die Ausnahmen abgefangen werden? Was ist der Sinn des Versuchs catch Block dann?

Und unten auf der Seite wir mit einem Schwanz rekursive Funktion in dem geschützten Abschnitt ein Beispiel haben ...

has_value(Val, Tree) -> 
    try has_value1(Val, Tree) of 
    false -> false 
    catch 
    true -> true 
    end. 

has_value1(_, {node, 'nil'}) -> 
    false; 
has_value1(Val, {node, {_, Val, _, _}}) -> 
    throw(true); 
has_value1(Val, {node, {_, _, Left, Right}}) -> 
    has_value1(Val, Left), 
    has_value1(Val, Right). 

Meint er, dass wir eine Funktion wrap Schwanz rekursive Code in eine Funktion verwenden, müssen wenn wir im geschützten Teil eines Versuches sind?

Antwort

11

Also können wir keine rekursiven Aufrufe in dem Teil, wo die Ausnahmen sind eingefangen? Was ist der Sinn des Versuchs catch Block dann?

Eine Funktion kann sich innerhalb eines try nicht rekursiv selbst aufrufen; oder, Tail-Optimierung wird nicht passieren, wenn dies der Fall ist. Wenn Sie try verwenden, müssen Sie in der Lage sein, an jedem beliebigen Punkt im Call-Stack zum Block catch zurückzuspringen. Was das bedeutet ist, dass es einen Call-Stack geben muss. Wenn die Tail Call-Optimierung verwendet wird, gibt es keine Funktionsaufrufe, da sie jetzt nur Schleifen sind. Es gibt nichts, zu dem man zurück springen könnte. Daher muss die Rekursion innerhalb eines try Blocks wirklich rekursiv sein.

Der Punkt entspricht in den meisten Sprachen den Ausnahmen. Die Tatsache, dass man nicht direkt recurse ist ein bisschen ein Ärgernis, aber sicherlich nicht die Nützlichkeit der Ausnahmebehandlung entfernen, weil:

meint er damit, dass wir eine Funktion verwenden müssen, um Schwanz rekursive Code wickeln in eine Funktion, wenn wir im geschützten Teil eines Versuches sind zu fangen ?

Ja. Alles was es braucht, ist eine zusätzliche Funktion und Sie können try ganz gut verwenden und trotzdem die Vorteile von TCO nutzen. Beispiel:

% No TCO 
func() -> 
    try 
    func() 
    catch _ -> 
    ok 
    end. 

% TCO 
func() -> 
    try 
    helper() 
    catch _ -> 
    ok 
    end. 

helper() -> helper(). 

Ich bin mir nicht sicher, ob es eine einfache Möglichkeit, um festzustellen, ob Sie versehentlich sind Rekursion, wenn Sie TCO erwarten passieren.Sie müssen wahrscheinlich nur wachsam sein, wenn Sie try verwenden.

0

Wenn der Tail-Call optimiert werden soll, muss dieser Aufruf außerhalb der try-catching-Klausel sein. Sie können die Konstruktion

your_fun(...) -> 
    ... 
    try ... of    <--- make notice of `of` 
     ... -> 
     some_call(...) 
    catch 
     ... 
    end. 

verwenden oder einfach den Aufruf nach der try-Klausel ausführen.

In Ihrem Code ist der Aufruf has_value1(Val, Right). optimiert, weil es der letzte Aufruf in der Funktion ist. Es spielt keine Rolle, ob es in diesem Fall innerhalb try Block genannt wird. Und Ausnahme wird nur verwendet, um einen frühen Ausgang von dieser Funktion und eine einfache Handhabung des Ergebnisses bereitzustellen.

Es ohne Ausnahmen neu geschrieben werden konnte, aber mit manueller Stapel Handhabung:

has_value(Val, Tree) -> 
    has_value(Val, [Tree]). 

has_value1(_, []) -> 
    false; 
has_value1(Val, [{node, 'nil'} | Stack]) -> 
    has_value1(Val, Stack); 
has_value1(Val, [{node, {_, Val, _, _}} | _]) -> 
    true; 
has_value1(Val, [{node, {_, _, Left, Right}} | Stack]) -> 
    has_value1(Val, [Left, Right | Stack]).