【问题标题】:Equality in Ocaml hashtablesOcaml 哈希表中的平等
【发布时间】:2012-01-23 13:42:57
【问题描述】:

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

我想要一个可以区分ab 的哈希表。这可能吗?

【问题讨论】:

  • 这种哈希比较在与不可变值一起使用时有点脆弱,如您的示例所示。文件说 (==) 的结果在这种情况下取​​决于实现:请参阅Pervasives module under Comparisons。理论上,编译器或运行时可以使任意两个相等的不可变值在物理上相等。
  • @JeffreyScofield 编译器或运行时也可能导致您期望物理上相等的值不同,这不是理论上的:let test x = let t = Array.make 1 x in x == t.(0) ;; test 1.0 ;; 计算 false。只存在于纸面上的 Caml 的多线程 GC 也可能会复制不可变的值。
  • 谢谢,这些都是非常有趣的例子,虽然我认为 OP 更关心硬币的另一面。您不能依赖具有唯一物理标识 (a != b) 的不可变值,除非它们不相等 (a b)。通常的解决方案(或我使用过的解决方案)是在您的值中保留一个唯一标识符。当然,这也有助于散列。

标签: hashtable ocaml equality


【解决方案1】:

您可以使用自定义哈希表:

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

然后在您的代码中使用H 而不是Hashtbl

【讨论】:

  • 当我将它粘贴到 Ocaml 的顶部循环中时,我得到一个语法错误(最后一个右括号带有下划线)。
  • 缺少end 关键字的结尾stuct
【解决方案2】:

Thomas and cago 的答案中的解决方案在功能上是正确的。如果您按原样使用他们的解决方案,可能会困扰您的一个问题是,如果您散列许多与(=) 相同且与(==) 不同的键,您将获得比预期更多的冲突。实际上,对于(=) 相等的所有键对于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);;

【讨论】:

  • 相等 (=) 值将散列到同一个桶的事实是一个真正的问题。如果您打算在哈希表中放置多个相等(但物理上不相等)的值,除非您使用不同的哈希函数,否则您会发现性能非常差。见stackoverflow.com/questions/6757509/…
  • 在较旧的 OCaml 版本中(我不知道最近是否发生了变化),只有次要 GC 或压缩可以移动块,所以如果你关闭压缩并强制,指针上的散列是安全的散列之前的次要 GC。并不是说我建议在您要维护的程序中这样做。
  • @Gilles 仍然如此。我几乎指出了这些信息,但随后反映出我可能已经比 OP 所期望的更具技术性。一些 GC 有 pinning,但这感觉像是解决这个问题的错误工具。 (==)-hashtables 可以通过将次要对象与主要对象分开并仅在必要时重新散列这些对象来实现。仍然需要关闭压缩或每次发生时重新哈希。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-02
  • 1970-01-01
  • 2011-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多