2012-01-23 8 views
6

Esistono hashtables in Ocaml che utilizzano == anziché = durante il test per l'uguaglianza di chiavi? Per esempio:Equality in Ocaml hashtables

# type foo = A of int;; 
# let a = A(1);; 
# let b = A(1);; 
# a == b;; 
- : bool = false 
# a = b;; 
- : bool = true 
# let h = Hashtbl.create 8;; 
# Hashtbl.add h a 1;; 
# Hashtbl.add h b 2;; 
# Hashtbl.find h a;; 
- : int = 2 
# Hashtbl.find h b;; 
- : int = 2 

Vorrei una tabella hash che può distinguere tra a e b. È possibile?

+0

Questo tipo di confronto di hash è un po 'fragile se utilizzato con valori immutabili, come nel tuo esempio. I documenti dicono che il risultato di (==) dipende dall'implementazione in quel caso: vedi il modulo [Pervasives sotto ** Comparisons **] (http://caml.inria.fr/pub/docs/manual-ocaml/libref/ Pervasives.html). In teoria, il compilatore o il runtime possono far sì che due valori uguali immutabili siano fisicamente uguali, se lo desiderano. –

+0

@JeffreyScofield Il compilatore o il runtime possono anche causare valori che ci si aspetterebbe essere fisicamente uguali a diversi, e ciò non è teorico: 'lascia test x = let t = Array.make 1 x in x == t. (0) ;; test 1.0 ;; 'compute' false'. Il GC multi-thread per Caml che esiste solo sulla carta può anche duplicare valori immutabili. –

+0

Grazie, sono esempi molto interessanti, anche se penso che l'OP sia più interessata all'altra faccia della medaglia. Non puoi dipendere da valori immutabili con identità fisiche uniche (a! = B) a meno che non siano uguali (a <> b). La solita soluzione (o, quella che ho usato) è mantenere un identificatore univoco nei valori. Questo aiuta anche con l'hashing, ovviamente. –

risposta

11

È possibile utilizzare hashtables personalizzati:

module H = Hashtbl.Make(struct 
    type t = foo 
    let equal = (==) 
    let hash = Hashtbl.hash 
end) 

e quindi utilizzare H invece di Hashtbl nel codice.

+0

quando lo incollo nel toploop di Ocaml, viene visualizzato un errore di sintassi (l'ultima parentesi chiusa è sottolineata). – pbp

+0

manca la chiusura 'end' alla parola chiave' stuct'. – nlucaroni

4

La soluzione nelle risposte di Thomas e Cago è funzionalmente corretta. Un problema che potrebbe causare problemi se si utilizza la propria soluzione così com'è è che si otterranno più collisioni del previsto se si hanno molte chiavi uguali per (=) e diverse per (==). Infatti, tutte le chiavi uguali per (=) hanno lo stesso hash per Hashtbl.hash e finiscono nello stesso bucket, dove sono riconosciute come diverse (dal momento che hai chiesto di utilizzare (==) come funzione di uguaglianza) e creare associazioni diverse. Nei casi peggiori, la tabella hash si comporterà con la stessa complessità di una lista di associazioni (che, a proposito, è un'altra struttura dati che potresti usare, e quindi non dovresti preoccuparti di fornire una funzione hash).

Se è possibile accettare la chiave di un valore che cambia occasionalmente (e quindi il valore è impossibile da recuperare dalla tabella hash, poiché l'associazione è quindi nel bucket errato), è possibile utilizzare la seguente funzione di basso livello come hash:

external address_of_value: 'a -> int = "address_of_value" 

Implementato in C come:

#include "caml/mlvalues.h" 

value address_of_value(value v) 
{ 
    return (Val_long(((unsigned long)v)/sizeof(long))); 
} 

Si potrebbe quindi utilizzare:

module H = Hashtbl.Make(struct 
    type t = foo 
    let equal = (==) 
    let hash = address_of_value 
end);; 
+1

Il fatto che i valori uguali (=) cancelleranno lo stesso bucket è un problema reale. Se stai pianificando di inserire più di un valore uguale (ma non fisicamente uguale) nella tua tabella hash, vedrai prestazioni molto scarse a meno che non usi una funzione hash diversa. Vedi http: // StackOverflow.it/questions/6757509/stackoverflow-with-specialist-hashtbl-via-hashtbl-make –

+1

Nelle versioni precedenti di OCaml (non so se è cambiato di recente), solo un GC o una compattazione minori potrebbero spostare un blocco, quindi hashing il puntatore era sicuro se si disattivava la compattazione e si costringeva un GC secondario prima dell'hashing. Non che lo consiglierei in un programma che vuoi mantenere. – Gilles

+0

@Gilles Questo è ancora il caso. Ho quasi sottolineato questa informazione, ma poi ho riflettuto che probabilmente ero già più tecnico dell'OP desiderato. Alcuni GC sono bloccati, ma sembra uno strumento sbagliato per questo problema. (==) - Gli hash possono essere implementati tenendo separati gli oggetti secondari da quelli principali e rilanciando solo questi se necessario. Sarebbe comunque necessario disattivare la compattazione o ripetere ogni volta che si verifica uno. –