Критика Эрланга - PullRequest
       6

Критика Эрланга

3 голосов
/ 18 апреля 2010

Я пытаюсь осмыслить некоторые базовые функции эрланга, и я мог бы сделать несколько замечаний по поводу следующего.

У меня есть следующий код erlang, который берет список кортежей и возвращает список минус элемент, если ключ найден:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    Acc;
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            [H| remove(T, Key, Acc)];
        true  -> 
            remove(T, Key, Acc)
    end.

Это хороший способ сделать это?

Оператор if кажется неправильным.

Также мое использование аккумулятора Acc делает этот хвост рекурсивным?

Ответы [ 2 ]

5 голосов
/ 18 апреля 2010

Нет, использование Acc не делает его рекурсивным.Ваша ветвь if возвращает [H| remove(T, Key, Acc)], который не является хвостовым вызовом, и эта ветвь будет использоваться большую часть времени.Если быть более точным, то использование Acc бесполезно, потому что это будет [] все время, вы вообще не меняете его значение.Правильный код должен выглядеть следующим образом.

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([H|T], Key, Acc) ->
    if
        element(1, H) /= Key ->             
            remove(T, Key, [H|Acc]);
        true  -> 
            remove(T, Key, Acc)
    end.

Но если члены вашего списка всегда являются парами, я бы предпочел прямое сопоставление с шаблоном:

delete(Key, Database) ->
    remove(Database, Key, []).

remove([], Key, Acc) ->
    lists:reverse(Acc);
remove([{Key, _}|T], Key, Acc) ->
    remove(T, Key, Acc);
% if it should delete only first occurrence then lists:reverse(Acc, T);
remove([H|T], Key, Acc) ->
    remove(T, Key, [H|Acc]).

Но я думаю, что это пример, где можно применить Миф: Хвост-рекурсивные функции НАМНОГО быстрее, чем рекурсивные функции , поэтому я бы использовал гораздо более простую рекурсивную версию:

delete(Key, []) -> [];
delete(Key, [{Key, _}|T]) -> delete(Key, T);
% if it should delete only first occurrence then just T;
delete(Key, [H|T]) -> [H | delete(Key, T)].
2 голосов
/ 18 апреля 2010

Как уже упоминалось, есть стандартная функция модуля, которая уже делает это (proplists: delete). Не нужно больше говорить, но ...

Я бы хотел сохранить исходное имя метода (удалить), но иметь локальную версию, включая аккумулятор в качестве параметра. Контекст заставляет меня думать, что порядок кортежей в «базе данных» не имеет значения, поэтому использование списков: обратный процесс не требуется.

-module(foo).
-export([delete/2]).

delete(Key, Database) ->
    delete(Key, Database, []).

delete(_Key, [], Acc) ->
    Acc;
delete(Key, [{Key, _} | T], Acc) ->
    delete(Key, T, Acc);
delete(Key, [Entry={_, _} | T], Acc) ->
    delete(Key, T, [Entry | Acc]).

Пара вещей здесь:

  • хвостовая рекурсия; в общем, я думаю, что безопаснее придерживаться хвостовой рекурсии - хотя существуют оптимизации для рекурсивных вызовов тела, вам действительно нужно провести некоторое измерение производительности с реалистичными (для вашего приложения) данными, чтобы сделать сравнение
  • обратите внимание, что мы не принимаем какой-либо старый список здесь; Соответствие шаблона ввода в delete / 3 помогает обеспечить это (в зависимости от того, для чего это нужно, вы можете или не хотите этого)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...