Как 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, сколько вы хотите, пока ничего не происходит после него.