Нет хвостового рекурсивного кода в блоке try catch?
Я читаю урок Эрланга в http://learnyousomeerlang.com/errors-and-exceptions
Я не понимаю эту часть:
выражение между try и of считается защищенным. Это означает, что любое исключение, происходящее в этом вызове, будет поймано.
и
защищенная часть исключения не может быть хвостовой способный к повторному использованию.
[...]
помещая рекурсивные вызовы между of и 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 ?
2 ответов
поэтому мы не можем поместить рекурсивные вызовы в ту часть, где исключения поймали ? Тогда в чем смысл блока 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
.
если вы хотите оптимизировать tail-call, этот вызов должен быть вне предложения try-catching. Вы можете использовать конструкцию
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]).