2010-07-22 4 views
10

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

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

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

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

+0

запоминания не опечатка. См. Http://en.wikipedia.org/wiki/Memoization –

+0

lol. Извините за это: D Вы всегда узнаете что-то новое: p –

ответ

4

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

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 generic server. В функции 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. 

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

+1

Точка примера будет такова, что если у вас есть 12! memoized, вы вычисляете 13! умножив это значение на 13. Однако ваш код рассчитает 13! с самого начала, независимо от того, что memoized. – Zed

+0

Я знаю об этом. Я предполагаю, что выбор состоит в том, чтобы хранить все частичные значения или только окончательное значение. Я совершенно новичок в «воспоминаниях». Пример просто хотел отобразить «кеширование» с помощью dets. –

+0

Даже если вы сохраните только окончательные значения, это будет частичным результатом для ваших последующих вычислений. Я просто хотел указать, что вы должны также проверять кешированные значения в вашей функции do_fact. – Zed

14

В зависимости от вашего дела, вы можете также использовать process dictionary для запоминания:

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. 
+0

Мне нравится это решение, простое и точное. –

+0

Я использую это для задач динамического программирования. Я надеюсь, что это эффективный способ и другие способы достижения memoization. То есть, надеясь получить и поставить O (1) операции в некоторые хэш. – Tommy

Смежные вопросы