2010-07-22 17 views
10

Suponga que tiene una función simple, que puede llegar a ser muy caro para grandes valores:ejemplo simple para Erlang memoization

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

¿Dónde puedo encontrar un ejemplo sencillo de almacenamiento en caché (o memoizing) valores de la función mediante el uso de dets?

Cualquier otra forma de memorizar fácilmente sería muy apreciada.

+0

memoization no es un error tipográfico. Ver - http://en.wikipedia.org/wiki/Memoization –

+0

lol. Perdón por eso: D Siempre aprendes algo nuevo: p –

Respuesta

4

La idea es que cada vez que solicite su cálculo pesado de, inmediatamente verifica la memoria caché si ya la ha evaluado. En caso afirmativo, simplemente devuelve el valor almacenado. De lo contrario, debe evaluar el nuevo valor y almacenarlo antes de devolverlo al usuario final.

A dict, en lugar de una tabla de dets, también podría funcionar.

(la siguiente solución es no probado)

-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). 

Es posible que desee encapsular todo el asunto en una Erlang generic server. En la función init debe crear la tabla DETS, la función fact/1 debe representar su API y debe implementar la lógica en las funciones handle_call.

Un mejor ejemplo podría ser escribir un servicio abreviado para URL, en caché.

Según lo sugerido por @Zed, tendría sentido almacenar los resultados parciales para evitar nuevos cálculos. Si este es el caso:

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

Obviamente, incluso si esto ayuda para un gran número, usted tiene que considerar el costo adicional de añadir una entrada a la tabla de consulta para cada paso. Teniendo en cuenta los motivos por los que se ha introducido el almacenamiento en caché (suponemos que el cálculo es muy pesado, por lo que el tiempo de inserción de búsqueda es insignificante), esto debería ser perfectamente correcto.

+1

¡El punto del ejemplo sería que si tienes 12! memoized, calcula 13! al multiplicar ese valor por 13. Sin embargo, su código calculará 13! desde el principio, sin importar lo que se memorice. – Zed

+0

Soy consciente de ello. Supongo que la elección allí es almacenar todos los valores parciales o solo el valor final. Soy completamente nuevo en "memoización". El ejemplo simplemente quería mostrar el "caché" usando dets. –

+0

Incluso si almacena solo los valores finales, esos serán resultados parciales para sus próximos cálculos. Solo quería señalar que también debe verificar los valores almacenados en caché en su función do_fact. – Zed

14

Dependiendo de su caso, también se puede utilizar el process dictionary para 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. 
+0

Me gusta esta solución, simple y al grano. –

+0

Lo he usado mucho para tipos de problemas de programación dinámica. Espero que sea eficiente en comparación con otras formas de lograr la memorización ... es decir, esperar que get y put sean O (1) operaciones en algo de hash. – Tommy