2013-07-09 24 views
7

我讀了二郎山教訓在http://learnyousomeerlang.com/errors-and-exceptionstry catch塊中沒有尾遞歸代碼?

我不明白這個部分:

表達的嘗試之間的被說成是受保護的。這意味着在該調用中發生的任何異常都會被捕獲。

而且

異常的受保護的部分不能是尾遞歸。

[...]

通過把的,趕上你之間的遞歸調用,你是不是在一個受保護的部分,你會從上次通話優化中受益。

因此,我們不能把遞歸調用放在捕捉異常的地方?那麼try catch塊有什麼意義呢?

及以下的頁面,我們必須與受保護節尾遞歸函數的一個例子...

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). 

他的意思是,我們需要使用的功能包尾遞歸代碼到一個函數當我們處於try catch的受保護部分時?

回答

11

因此,我們不能把遞歸調用放在除了 之外的地方?那麼try catch塊有什麼意義呢?

函數不能在遞歸調用try;或者更確切地說,尾部優化不會發生。當您使用try時,您必須能夠在調用堆棧下的任意位置跳回catch塊。這意味着必須有的調用堆棧。如果使用尾部調用優化,則不存在函數調用,因爲它們現在只是循環。沒有什麼可以跳回來的。因此,try塊內的遞歸必須真正遞歸。

這一點與大多數語言的例外情況相同。事實上,你不能直接遞歸是一個有點討厭,但絕對不會刪除的異常處理程序,因爲:

他是否意味着我們需要使用的功能包尾遞歸 碼成一個函數,當我們在一個try catch的受保護部分 ?

是的。所需要的只是一個額外的功能,您可以使用try就好了,仍然可以獲得TCO的好處。例如:

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

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

helper() -> helper(). 

我不知道如果有一個簡單的方法來確定你的時候你希望TCO發生意外會遞歸。使用try時,您可能只需保持警惕。

0

如果您希望優化尾部呼叫,那麼該呼叫必須超出try-catch條款。您可以使用建設

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

或只是在try子句後進行調用。

在您的代碼中,調用has_value1(Val, Right).進行了優化,因爲它是該函數中的最後一個調用。在這種情況下是否在try區塊中調用它並不重要。而異常僅用於提供早期退出此功能和簡單處理結果。

可以毫不例外被改寫,但使用人工棧處理:

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]).