try catch块中没有尾递归代码?

时间:2013-07-09 23:22:32

标签: erlang

我正在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的受保护部分时,他是否意味着我们需要使用函数将尾递归代码包装到函数中?

2 个答案:

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