Простой пример для memoization Erlang

Предположим, что у вас есть простая функция, которая может стать довольно дорогой для больших значений:

fact(0) -> 1;
fact(N) -> N * fact(N - 1).

Где я могу найти простой пример значений функции кеширования (или memoizing) с помощью dets?

Любые другие способы легкого запоминания будут высоко оценены.

Ответ 1

Идея состоит в том, что каждый раз, когда вы запрашиваете свой тяжелый расчет, вы сразу же проверяете кеш, если вы уже его оценили. Если да, вы просто возвращаете сохраненное значение. Если нет, вы должны оценить новое значение и сохранить его, прежде чем возвращать его конечному пользователю.

A dict, а не таблица dets также может работать.

(следующее решение не проверено)

-module(cache_fact).

-export([init/0, fact/1]).

init() ->
    {ok, _} = dets:open_file(values, []).

fact(N) ->
    case dets:lookup(values, N) of
      [] ->
        Result = do_fact(N), 
        dets:insert_new(values, {N, Result}),
        Result;
      [{N, Cached}] ->
        Cached
    end.

do_fact(0) ->
    1;
do_fact(N) ->
    N * do_fact(N-1).

Возможно, вы захотите инкапсулировать все это в общий сервер Erlang. В функции init вы должны создать таблицу DETS, функция fact/1 должна представлять ваш API, и вы должны реализовать логику в функциях handle_call.

Более приятным примером может быть запись службы сокращения для URL-адресов, кэшированных.

Как было предложено @Zed, было бы целесообразно сохранить частичные результаты, чтобы избежать дальнейших повторных вычислений. Если это так:

-module(cache_fact).

-export([init/0, fact/1]).

init() ->
    {ok, _} = dets:open_file(values, []).

fact(0) ->
    1;
fact(N) ->
    case dets:lookup(values, N) of
      [] ->
        Result = N * fact(N-1),
        dets:insert_new(values, {N, Result}),
        Result;
      [{N, Cached}] ->
        Cached
    end.

Очевидно, что даже если это помогает для больших чисел, вы должны учитывать дополнительные затраты на добавление записи в таблицу поиска для каждого шага. Учитывая причины, по которым было введено кэширование (мы предполагаем, что вычисление очень тяжелое, поэтому время вставки поиска незначительно), это должно быть совершенно нормально.

Ответ 2

В зависимости от вашего случая вы также можете использовать словарь процессов для memoization:

fact(0) -> 1;
fact(N) ->
    case erlang:get({'fact', N}) of
        F when is_integer(F) ->
            F;
        'undefined' ->
            F = N * fact(N-1),
            erlang:put({'fact', N}, F),
            F
    end.