Как erlang обрабатывает регистр событий с хвостовой рекурсией
Скажем, у меня есть этот код здесь:
do_recv_loop(State) ->
receive
{do,Stuff} ->
case Stuff of
one_thing ->
do_one_thing(),
do_recv_loop(State);
another_thing ->
do_another_thing(),
do_recv_loop(State);
_ ->
im_dead_now
end
{die} -> im_dead_now;
_ -> do_recv_loop(State)
end.
Теперь теоретически это хвостовое рекурсивно, так как ни один из трех вызовов do_recv_loop не требует возврата. Но признает ли erlang, что это хвост рекурсивный и оптимизирован соответствующим образом? Я обеспокоен тем, что вложенная структура может не распознать ее.
Ответы
Ответ 1
Да, будет. Erlang требуется для оптимизации хвостовых вызовов, и это, безусловно, хвостовой вызов, поскольку ничего не происходит после вызова функции.
Раньше я хотел, чтобы в Erlang было ключевое слово tailcall
, поэтому компилятор мог предупредить меня о недопустимых целях использования, но потом я привык к нему.
Ответ 2
Это, я думаю, актуально, потому что вы спрашиваете, как вы знаете, оптимизирована ли ваша рекурсивная функция компилятором. Поскольку вы не используете списки: reverse/1, ниже может не применяться, но для кого-то другого с тем же вопросом, но с другим примером кода это может быть очень актуально.
Из восьми мифов о производительности Эрланга в Руководстве по эффективности Erlang
В версиях R12B и более поздних версий оптимизация, которая будет во многих случаев уменьшает количество используемых слов в стеке в рекурсивных вызовах, так что функция рекурсивного списка тел и хвостовую рекурсивную функцию, которая вызывает списков: reverse/1 в конце будет использовать точно такой же объем памяти.
http://www.erlang.org/doc/efficiency_guide/myths.html#id58884
Я думаю, что отвлекающее сообщение состоит в том, что вам, возможно, придется в некоторых случаях измерить, чтобы увидеть, что будет лучше.
Ответ 3
Да, это хвост рекурсивный. Главное, о чем нужно знать, - это если вы обернуты внутри исключений. В этом случае иногда исключение должно жить в стеке, и это сделает что-то, что выглядит хвостом-рекурсивным во что-то обманчиво не так.
Оптимизация хвостового вызова применима, если вызов находится в хвостовом положении. Положение хвоста - это "последняя вещь перед возвратом функции". Заметим, что в
fact(0) -> 1;
fact(N) -> N * fact(N-1).
рекурсивный вызов факту не находится в хвостовом положении, потому что после вычисления fact(N-1)
вам нужно запустить продолжение N * _
(т.е. умножить на N
).
Ответ 4
Я новичок в Erlang, но из того, что я собрал, это правило, похоже, заключается в том, что для того, чтобы быть хвостовым рекурсивным, функция должна делать одну из двух вещей в любой заданной логической ветке:
- не делать рекурсивный вызов
- возвращает значение рекурсивного вызова и ничего не делает после него
Этот рекурсивный вызов может быть вложен в столько вызовов if
, case
, или receive
, сколько вы хотите, пока ничего не происходит после него.