我正在http://learnyousomeerlang.com/errors-and-exceptions
阅读Erlang课程我不明白这一部分:
try和of之间的表达被认为是受保护的。这意味着将捕获该调用中发生的任何类型的异常。
和
异常的受保护部分不能是尾递归。
[...]
通过在of和catch之间进行递归调用,您不在受保护的部分,并且您将受益于Last Call Optimization。
所以我们不能在捕获异常的部分放置递归调用?那么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的受保护部分时,他是否意味着我们需要使用函数将尾递归代码包装到函数中?
答案 0 :(得分:12)
所以我们不能在异常部分放置递归调用 抓住了?那么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
时,您可能只需要保持警惕。
答案 1 :(得分: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]).