5

我注意到 Clojure (1.4) 似乎乐于考虑向量等于seq同一向量的 ,但同样不适用于地图:

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

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

为什么要=以这种方式不同的行为?

4

5 回答 5

12

Clojure=可以被认为是分两步进行比较:

  1. 检查被比较的事物的类型是否属于相同的“相等分区”,即成员可能相等的一类类型(取决于给定数据结构的确切成员之类的东西,但不是特定类型分区);

  2. 如果是这样,请检查被比较的事物是否实际上是相等的。

一种这样的平等划分是“顺序”事物的划分。向量被认为是连续的:

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

各种类型的序列也是如此:

(instance? clojure.lang.Sequential (seq {1 2}))
;= true

因此,当(且仅当)其对应元素相等时,向量才被视为等于 seq。

(请注意,(seq {})产生nil,它不是连续的,并且将“不等于”与()等进行比较[]

另一方面,映射构成了它们自己的相等分区,因此虽然哈希映射可能被认为等于排序映射,但它永远不会被认为等于序列。特别是,它不等于其条目的 seq,这是(seq some-map)产生的。

于 2012-06-13T00:37:09.730 回答
5

我想这是因为在序列中的顺序以及特定位置的值很重要,而在 map 中,键/值的顺序无关紧要,语义之间的这种差异会导致它如示例代码所示那样工作。

有关更多详细信息,请查看mapEquals文件https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java

它检查另一个对象是否不是映射,然后返回 false。

于 2012-06-12T11:28:07.340 回答
3
user=> (seq {1 2})
([1 2])
user=> (type {1 2})
clojure.lang.PersistentArrayMap
于 2012-06-12T11:31:50.273 回答
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)

但他们不被视为平等用户> (= {1 2} (seq {1 2})) false

对于较长的地图也是如此:

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,这是(我认为)安全的,因为 seq 函数总是以相同的方式迭代整个数据结构并且结构是不可变的值aseq的 aseq是 aseq

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
于 2012-06-12T17:18:39.813 回答
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].

于 2012-06-13T14:32:16.470 回答