Равенство в хеш-таблицах Ocaml

Существуют ли в 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

Мне нужна хеш-таблица, которая может различать a и b. Это возможно?


person pbp    schedule 23.01.2012    source источник
comment
Такое сравнение хэшей немного хрупкое при использовании с неизменяемыми значениями, как в вашем примере. В документах говорится, что в этом случае результат (==) зависит от реализации: см. модуль Pervasives в разделе Сравнения. Теоретически компилятор или среда выполнения могут сделать любые два одинаковых неизменяемых значения физически равными, если захотят.   -  person Jeffrey Scofield    schedule 23.01.2012
comment
@JeffreyScofield Компилятор или среда выполнения также могут привести к тому, что значения, которые, как вы ожидаете, будут физически равными, будут разными, и это не теоретически: let test x = let t = Array.make 1 x in x == t.(0) ;; test 1.0 ;; вычисляет false. Многопоточный сборщик мусора для Caml, который существует только на бумаге, также может дублировать неизменяемые значения.   -  person Pascal Cuoq    schedule 23.01.2012
comment
Спасибо, это очень интересные примеры, хотя я думаю, что ОП больше интересует другая сторона медали. Вы не можете зависеть от неизменяемых значений, имеющих уникальные физические идентификаторы (a != b), если только они не равны (a ‹› b). Обычное решение (или то, которое я использовал) состоит в том, чтобы сохранить уникальный идентификатор в ваших значениях. Это также помогает с хешированием, конечно.   -  person Jeffrey Scofield    schedule 23.01.2012


Ответы (2)


Вы можете использовать пользовательские хэш-таблицы:

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

А затем используйте H вместо Hashtbl в своем коде.

person Thomas    schedule 23.01.2012
comment
когда я вставляю это в топлуп Ocaml, я получаю синтаксическую ошибку (последняя закрывающая скобка подчеркнута). - person pbp; 23.01.2012
comment
отсутствует закрывающий end ключевого слова stuct. - person nlucaroni; 23.01.2012

Решение в ответах Томаса и Каго функционально правильное. Одна проблема, которая может беспокоить вас, если вы используете их решение как есть, заключается в том, что вы получите больше коллизий, чем ожидалось, если вы хэшируете много ключей, одинаковых для (=) и разных для (==). Действительно, все ключи, которые равны для (=), имеют одинаковый хэш для Hashtbl.hash и попадают в одно и то же ведро, где они распознаются как разные (поскольку вы просили использовать (==) в качестве функции равенства) и создают разные привязки. В худшем случае хэш-таблица будет вести себя так же сложно, как и список ассоциаций (который, кстати, является еще одной структурой данных, которую вы могли бы использовать, и тогда вам не пришлось бы беспокоиться о предоставлении хеш-функции). ).

Если вы можете допустить, что ключ значения время от времени меняется (и, следовательно, значение невозможно получить из хеш-таблицы, поскольку тогда привязка находится в неправильном сегменте), вы можете использовать следующую низкоуровневую функцию в качестве хэша:

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

Реализовано на C как:

#include "caml/mlvalues.h"

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

Затем вы должны использовать:

module H = Hashtbl.Make(struct
  type t = foo 
  let equal = (==) 
  let hash = address_of_value
end);;
person Pascal Cuoq    schedule 23.01.2012
comment
Тот факт, что одинаковые (=) значения будут хэшироваться в одно и то же ведро, является реальной проблемой. Если вы планируете поместить более нескольких одинаковых (но не физически равных) значений в свою хеш-таблицу, вы увидите очень низкую производительность, если не используете другую хеш-функцию. См. stackoverflow.com/questions/ 6757509/ - person Jeffrey Scofield; 23.01.2012
comment
В более старых версиях OCaml (я не знаю, изменилось ли это в последнее время) только второстепенный GC или уплотнение могли переместить блок, поэтому хеширование указателя было безопасным, если вы отключили уплотнение и принудительно выполнили второстепенный GC перед хешированием. Не то чтобы я рекомендовал это в программе, которую вы хотите поддерживать. - person Gilles 'SO- stop being evil'; 24.01.2012
comment
@ Жиль Это все еще так. Я почти указал на эту информацию, но затем подумал, что я, вероятно, уже был более техническим, чем того хотел ОП. Некоторые сборщики мусора имеют закрепление, но это кажется неправильным инструментом для решения этой проблемы. (==)-хэш-таблицы могут быть реализованы путем хранения второстепенных объектов отдельно от основных и перефразирования только их по мере необходимости. По-прежнему необходимо либо отключать сжатие, либо перефразировать каждый раз, когда оно происходит. - person Pascal Cuoq; 24.01.2012