Clojure中集合与序列的相等性

5

我注意到Clojure(1.4)似乎很高兴地将向量视为相同向量的seq,但对于映射则不适用:

(= [1 2] (seq [1 2]))
=> true

(= {1 2} (seq {1 2}))
=> false

为什么=的行为会以这种方式不同呢?
5个回答

12

Clojure中的=可以理解为通过两个步骤进行比较:

  1. 检查被比较的对象类型是否属于同一“相等划分”类,即一组可能相等的类型(根据给定数据结构的具体成员而不是分区中的特定类型);

  2. 如果是,则检查被比较的对象是否实际上相等。

“顺序”是其中一个相等划分。向量被认为是顺序的:

(instance? clojure.lang.Sequential [])
;= true

As是各种类型的序列:

(instance? clojure.lang.Sequential (seq {1 2}))
;= true
因此,只有当向量的对应元素相等时,向量才被认为等于序列。(请注意,(seq {})会产生nil,它不是顺序的并且与()[]等比较时会“不相等”)。另一方面,映射构成自己的等价划分,因此虽然哈希映射可能被认为等于排序映射,但它永远不会被认为等于序列。特别地,它不等于其条目的序列,这就是(seq some-map)产生的内容。

5

3
user=> (seq {1 2})
([1 2])
user=> (type {1 2})
clojure.lang.PersistentArrayMap

2

我认为这个例子展示了Clojure中关于值相等性的一个微小不一致,因为它们是通过seq函数从同一类型派生出来的不同类型。人们可以认为这并不矛盾,因为它比较的是派生类型和它所派生的类型。如果使用向量(请参见底部注释)在同样的例子上应用相同的逻辑,那么这种说法可能是正确的。

内容是相同类型的:

user> (type (first (seq {1 2})))
clojure.lang.MapEntry
user> (type (first {1 2}))
clojure.lang.MapEntry

user> (= (type (first {1 2})) (type (first (seq {1 2}))))
true
user> (= (first {1 2}) (first (seq {1 2})))
true

这些序列具有相同的值。

user> (map = (seq {1 2}) {1 2})
(true)

但它们并不被视为相等 user> (= {1 2} (seq {1 2})) 错误

对于更长的映射也是如此:

user> (map = (seq {1 2 3 4}) {1 2 3 4})
(true true)
user> (map = (seq {1 2 3 4 5 6}) {1 2 3 4 5 6})
(true true true)
user> (map = (seq {9 10 1 2 3 4 5 6}) {9 10 1 2 3 4 5 6})
(true true true true)    

即使它们的顺序不同,仍然有效。
user> (map = (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
(true true true true)

但是如果包含的类型不同,就不行了 :-(

user> (= {1 2 3 4} (seq {1 2 3 4}))
false

编辑:这并不总是正确的,请参见下面: 为了解决这个问题,您可以在比较之前将所有内容转换为序列,这是安全的(我想)因为seq函数总是以相同的方式迭代整个数据结构,而且结构是不可变的值,一个seqseq是一个seq

user> (= (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
false
user> (= (seq {9 10 1 2 3 4 5 6}) (seq {1 2 3 4 5 6 9 10}))
true


向量被处理方式不同:

user> (= [1 2 3 4] (seq [1 2 3 4]))
true

也许理解语言中的细微差别是学习语言的一部分,或者有朝一日这种情况会改变(但我不抱太大希望)
编辑:
我发现两张地图对同一个值产生不同的顺序,因此仅仅调用映射上的seq方法并不能给你正确的映射相等性。
user> (seq (zipmap  [3 1 5 9][4 2 6 10]))
([9 10] [5 6] [1 2] [3 4])
user> (seq {9 10 5 6 1 2 3 4})
([1 2] [3 4] [5 6] [9 10])
user> 

这是一个我所说的正确的地图相等性的示例:
user> (def a (zipmap  [3 1 5 9][4 2 6 10]))
#'user/a
user> (def b {9 10 5 6 1 2 3 4})
#'user/b
user> (every? true? (map #(= (a %) (b %)) (keys a)))
true

1

(seq some-hash-map) 会给你一个条目序列(键/值对)。

例如:

foo.core=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:c 3] [:b 2])

这与[:a 1 :b 2 :c 3]不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接