2012-01-23 18 views
6

¿Hay tablas hash en Ocaml que usan == en lugar de = al probar la igualdad de teclas? Por ejemplo:Igualdad en tablas hash Ocaml

# 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 

me gustaría una tabla hash que pueden distinguir entre a y b. ¿Es eso posible?

+0

Este tipo de comparación hash es un poco frágil cuando se usa con valores inmutables, como en su ejemplo. Los documentos dicen que el resultado de (==) depende de la implementación en ese caso: ver el [módulo de Pervasives en ** Comparaciones **] (http://caml.inria.fr/pub/docs/manual-ocaml/libref/ Pervasives.html). En teoría, el compilador o el tiempo de ejecución pueden hacer que dos valores inmutables iguales sean físicamente iguales si lo desean. –

+0

@JeffreyScofield El compilador o el tiempo de ejecución también pueden hacer que los valores que esperaría ser físicamente iguales sean diferentes, y eso no es teórico: 'let test x = let t = Array.make 1 x in x == t. (0) ;; prueba 1.0 ;; 'calcula' falso'. El GC de múltiples hilos para Caml que solo existe en papel también puede duplicar valores inmutables. –

+0

Gracias, estos son ejemplos muy interesantes, aunque creo que OP está más preocupado con la otra cara de la moneda. No puede depender de valores inmutables que tengan identidades físicas únicas (a! = B) a menos que no sean iguales (a <> b). La solución habitual (o la que he usado) es mantener un identificador único en sus valores. Esto también ayuda con el hash, por supuesto. –

Respuesta

11

Puede utilizar tablas hash personalizados:

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

y luego usar H en lugar de Hashtbl en el código.

+0

cuando pego esto en el toploop de Ocaml, obtengo un error de sintaxis (el último paréntesis de cierre está subrayado). – pbp

+0

falta el 'final' de cierre a la palabra clave' stuct'. – nlucaroni

4

La solución en las respuestas de Thomas y cago es funcionalmente correcta. Un problema que puede molestarlo si utiliza su solución como está es que obtendrá más colisiones de las esperadas si tiene muchas claves que son iguales para (=) y diferentes para (==). De hecho, todas las claves que son iguales para (=) tienen el mismo hash para Hashtbl.hash, y terminan en el mismo depósito, donde se reconocen como diferentes (ya que solicitó (==) para usarse como función de igualdad) y crean enlaces diferentes. En el peor de los casos, la tabla hash se comportará con la misma complejidad que una lista de asociación (que, dicho sea de paso, es otra estructura de datos que podría estar utilizando, y entonces no tendría que preocuparse de proporcionar una función hash)

Si puede aceptar la clave de un valor que cambia ocasionalmente (y por lo tanto el valor es imposible de recuperar de la tabla hash, ya que el enlace está en el cubo incorrecto), puede usar la siguiente función de bajo nivel como picadillo:

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

Implementado en C como:

#include "caml/mlvalues.h" 

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

a continuación, utilizar:

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

El hecho de que los valores iguales (=) hagan hash en el mismo cubo es un problema real. Si planea colocar más de unos pocos valores iguales (pero no físicamente iguales) en su tabla hash, verá un rendimiento muy bajo a menos que use una función hash diferente. Ver http: // stackoverflow.com/questions/6757509/stackoverflow-with-specialized-hashtbl-via-hashtbl-make –

+1

En versiones anteriores de OCaml (no sé si eso ha cambiado recientemente), solo un GC menor o una compactación podrían mover un bloque, por lo que hash en el puntero era seguro si apagaba la compactación y forzaba un GC menor antes del hash. No es que lo recomiende en un programa que quiera mantener. – Gilles

+0

@Gilles Ese sigue siendo el caso. Casi apunto esta información, pero luego reflejé que probablemente ya estaba siendo más técnico que el OP deseado. Algunos GC tienen pinning, pero parece una herramienta incorrecta para este problema. (==) - las tablas hash se podrían implementar manteniendo los objetos menores separados de los principales y reafilando solo estos según sea necesario. Todavía sería necesario apagar la compactación o volver a generar cada vez que ocurra uno. –