【问题标题】:Common Lisp SXHASH and nested listsCommon Lisp SXHASH 和嵌套列表
【发布时间】:2021-12-25 04:45:06
【问题描述】:

standard 这么说 (equal x y) implies (= (sxhash x) (sxhash y))。让我们检查一下:

(defun sxhash-test ()
  (let ((obj1 (list 1 2 (list 1 1)))
        (obj2 (list 1 2 (list 1 2))))
    (format t "are objects equal?: ~a~%" (equal obj1 obj2)) ;; => NIL
    (format t "are their hashes equal?: ~a~%"(= (sxhash obj1) (sxhash obj2))))) ;; => T

函数equal 按预期工作,但sxhash 没有。你能解释一下我做错了什么吗?我使用 SBCL 2.1.9。

谢谢。

【问题讨论】:

  • 基本逻辑:x implies y不代表not x implies not y
  • 可能的列表比散列值多得多。所以碰撞是可能的。
  • sxhash 是否遍历树来创建哈希?似乎并非如此。 (list 1 2 3)(list 1 2 4) 创建不同的哈希值。

标签: common-lisp hash-function


【解决方案1】:

sxhash 必须满足四个要求:

  1. equal 的对象(因此eqleq 的对象,但不一定是equalp 将具有相同的sxhash 值;
  2. 对象的sxhash 值在单个图像的生命周期内不得更改,除非对象更改的方式不会使其equal 成为更改前的副本;
  3. 图像之间具有明确定义的相似性概念的各种类型的对象,那么它们的sxhash 值在每张图像中必须相同。
  4. sxhash 的计算必须始终终止。

(还有一个模糊的要求是“成为一个好的哈希码”)。

(1) 表示不是equal 的两个对象可能有相同的代码但可能没有,但是两个 equal 的对象必须 有相同的值。 sxhash 的一个糟糕但可能的实现是:

(defun sxhash/terrible (it)
  (declare (ignore it))
  0)

这未通过“成为一个好的哈希码”测试,但这并不是真正可以强制执行的。

您看到的是,不是equal 的两个对象确实具有相同的sxhash 值:这很好。

确实,(1) 和 (4) 意味着如果一个实现要在 conses 上计算 sxhash 以使其遍历图形,那么它必须非常小心:它要么需要发生检查或只需要深入。

但是,sxhash 很可能会下降到 cons 树中。例如,LispWorks 就是这样做的:

> (sxhash '(1 2 3))
11890816076270616

> (sxhash '(1 2 3 (4)))
369102953153702944

> (sxhash '(1 2 3 (4)))
740958182301008344

> (sxhash '(1 2 3 (5)))
740958455027237144

> (sxhash '(1 2 3 (5 6)))
741326672350173760

> (sxhash '(1 2 3 (5 (6))))
925006242171775434

同样,sxhash 将给定结构类的所有实例(或standard-class 的给定实例)视为具有相同的值,这也是相当合理的,因为这样的对象的地址不是恒定的并且没有存储哈希码的明显位置,无需烧录内存。但这绝不是要求。

【讨论】:

    【解决方案2】:

    观察到这种效果的原因是需要两个条件才能使值成为equal

    1. 这两个值相同,表示具有相同的哈希值。
    2. 这两个值具有相同的地址

    测试的两个列表具有相同的哈希,因为 sxhash 不遵循嵌套。事实上,两个结构总是有相同的哈希值。

     (sxhash (list 1 2 3)); => 3971322300187561939
     (sxhash (list 1 2 3)); => 3971322300187561939 (so, repeatable)
     (sxhash (list 1 2 3 (list 4))) ; => 3180777146619076709
     (sxhash (list 1 2 3 (list 5))) ; => 3180777146619076709 (ok ...)
    

    Why does `sxhash` return a constant for all structs?

    至于相同的地址,如果我创建两个像'a 这样的值,它们实际上是一个具有一个地址的项目,并且只在第一次看到它时存储。而(list 1 2 (list 1 1))(list 1 2 (list 1 2)) 是不同的东西,存储在不同的地址。

    (sb-kernel:get-lisp-obj-address 'a) ; => 68772678703
    (sb-kernel:get-lisp-obj-address 'a) ; => 68772678703 (same...)
    (sb-kernel:get-lisp-obj-address (list 1 2 3 (list 4))) ; => 68772925863
    (sb-kernel:get-lisp-obj-address (list 1 2 3 (list 5))) ; => 68772805335
    

    使用 sxhash 测试这两个列表是否相等通过,但由于地址不同而失败。

    【讨论】:

    • 这是不正确的。 sxhash 很可能会进入列表。结构的东西也依赖于实现。
    猜你喜欢
    • 2015-01-17
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-11
    • 2012-01-20
    • 1970-01-01
    相关资源
    最近更新 更多