2012-04-07 6 views
7

Sto cercando un modo per memoizzare i risultati di una funzione OCaml f che richiede due parametri (o più, in generale). Inoltre (e questa è la parte difficile), voglio che la mappa sottostante questo processo dimentichi del tutto un risultato se uno dei valori per i due parametri è garbage collection.Risultato di memoizing debole della funzione multiparametro in OCaml

Per una funzione che richiede esattamente un argomento, questo può essere fatto con il modulo Weak e il suo functor Make in modo semplice. Per generalizzare questo a qualcosa che può memoizzare le funzioni di arità superiore, una soluzione ingenua è creare una mappa debole da tuple di valori a valori di risultato. Ma questo non funzionerà correttamente rispetto alla garbage collection, poiché la tupla di valori esiste solo nell'ambito della funzione di memoization, non il codice client che chiama f. In effetti, il riferimento debole sarà alla tupla, che verrà raccolta dopo la memoizzazione (nel peggiore dei casi).

C'è un modo per farlo senza reimplementare Weak.Make?

Hash-consing è ortogonale alle mie esigenze ed è, in effetti, non proprio desiderabile per i miei valori.

Grazie!

risposta

3

Invece di indicizzazione da parte tuple si potrebbe avere una struttura ad albero. Avresti una tabella debole indicizzata dal primo parametro di funzione le cui voci sono tabelle secondarie deboli. Le tabelle secondarie dovrebbero essere indicizzate dal secondo parametro di funzione e contenere i risultati memorizzati. Questa struttura dimenticherà i risultati della funzione memoizzata non appena il parametro di funzione è GCed. Tuttavia, le tabelle secondarie verranno mantenute fino a quando il primo parametro di funzione è attivo. A seconda delle dimensioni dei risultati della tua funzione e della distribuzione di diversi primi parametri, questo potrebbe essere un compromesso ragionevole.

Non l'ho ancora provato. Sembra anche abbastanza ovvio.

+0

Posso vedere come la garbage collection del primo valore del parametro risulterebbe nel liberare la tabella corrispondente per il secondo parametro.Tuttavia, GCing di un valore in una tabella per il secondo parametro non fa nulla per il suo genitore (se viene utilizzato il modulo 'Weak'), anche se la mappa risultante è vuota. Ovviamente ciò potrebbe essere fatto analizzando attivamente il contenuto della mappa ed eliminando i primi parametri chiave che si associano alle tabelle vuote. – Nikos

+0

Giusto, come ho detto che la tabella secondaria non verrà raccolta fino alla liberazione del primo parametro. Ma il valore di ritorno memorizzato sarebbe stato raccolto (mi sembra). –

3

Un'idea è quella di eseguire la propria raccolta dei rifiuti.

Per semplicità, supponiamo che tutti gli argomenti abbiano lo stesso tipo k.

Oltre alla tabella debole principale contenente i risultati memoizzati con k * k, creare una tabella secondaria secondaria contenente argomenti singoli di tipo k. L'idea è di scansionare il tavolo principale una volta ogni tanto e rimuovere i binding che non sono più ricercati. Questo viene fatto cercando gli argomenti nella tabella secondaria; quindi se qualcuno di loro è andato rimuovi la rilegatura dalla tabella principale.

(Disclaimer: non ho ancora testato questo, ma non può funzionare o ci può essere soluzioni migliori)

+0

Buon punto. Forse è necessario un solo tavolo, uno che abbia tuple di riferimenti deboli come chiavi e che sia garbage personalizzato raccolto ogni tanto ogni volta che qualsiasi riferimento debole nella tupla della chiave scompare. Questo può essere fatto attraverso i finalizzatori? – Nikos

1

So che questa è una vecchia domanda, ma i miei colleghi hanno recentemente sviluppato una libreria di calcolo incrementale, chiamata Adapton, in grado di gestire questa funzionalità. È possibile trovare il codice here. Probabilmente vuoi usare il funtore LazySABidi (gli altri sono per il benchmarking). Puoi cercare nella cartella Applicazioni esempi su come usare la libreria. Fammi sapere se hai altre domande.