2010-07-22 13 views
10

Supponiamo di avere una funzione semplice, che può diventare piuttosto costoso per valori di grandi dimensioni:Semplice esempio per Erlang Memoizzazione

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

Dove posso trovare un semplice esempio di caching (o memoizing) i valori della funzionalità utilizzando dets?

Qualsiasi altro modo per una facile memorizzazione sarebbe molto apprezzato.

+0

Memoizzazione non è un errore di battitura. Vedi - http://en.wikipedia.org/wiki/Memoization –

+0

lol. Mi dispiace per questo: D impari sempre qualcosa di nuovo: p –

risposta

4

L'idea è che ogni volta che chiedi il tuo pesante calcolo pesante, controlli immediatamente nella cache se lo hai già valutato. In caso affermativo, è sufficiente restituire il valore memorizzato. In caso contrario, è necessario valutare il nuovo valore e memorizzarlo, prima di restituirlo all'utente finale.

A dict, invece di una tabella di dets, potrebbe funzionare anche.

(la seguente soluzione è testato)

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

si potrebbe desiderare di incapsulare il tutto in un Erlang generic server. Nella funzione init dovresti creare la tabella DETS, la funzione fact/1 dovrebbe rappresentare la tua API e dovresti implementare la logica nelle funzioni handle_call.

Un esempio migliore potrebbe essere la creazione di un servizio di riduzione degli URL per gli URL, memorizzato nella cache.

Come suggerito da @Zed, sarebbe opportuno memorizzare anche i risultati parziali per evitare ulteriori ricalcoli. Se questo è il 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. 

Ovviamente, anche se questo aiuta per i grandi numeri, è necessario considerare il costo aggiuntivo di aggiungere una voce alla tabella di ricerca per ogni passo. Considerando i motivi per cui è stata introdotta la memorizzazione nella cache (supponiamo che il calcolo sia molto pesante, quindi il tempo di inserimento della ricerca è insignificante), questo dovrebbe essere perfettamente soddisfacente.

+1

Il punto dell'esempio sarebbe che se ne hai 12! memoized, calcoli 13! moltiplicando quel valore per 13. Tuttavia il tuo codice calcolerà 13! dall'inizio, indipendentemente da ciò che è memoized. – Zed

+0

Ne sono consapevole. Immagino che la scelta sia quella di memorizzare tutti i valori parziali o solo il valore finale. Sono completamente nuovo alla "memoizzazione". L'esempio voleva semplicemente visualizzare il "caching" usando i dets. –

+0

Anche se memorizzi solo i valori finali, quelli saranno risultati parziali per i tuoi calcoli successivi. Volevo solo sottolineare che dovresti controllare anche i valori memorizzati nella cache nella tua funzione do_fact. – Zed

14

A seconda del caso, è anche possibile utilizzare il process dictionary per Memoizzazione:

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

Mi piace questa soluzione, semplice e al punto. –

+0

Utilizzo intensamente di questo tipo per problemi di programmazione dinamica. Spero che sia efficiente rispetto ad altri modi per ottenere la memoizzazione ... cioè, sperando di ottenere e mettere le operazioni di O (1) in qualche hash. – Tommy