我正在编写自己的实现range
的{{1}}。我对ISeq
的最初实现只是在我的范围和其他列表上调用equiv
,然后使用seq
进行比较:
=
这似乎很好,但是随后我遇到了一些奇怪的行为:
(defn equals? [this-range other-range]
(= (seq this-range) (seq other-range)))
(= (new-range 5 10)
(range 5 10))
=> true
(= (range 5 10)
(new-range 5 10))
=> false ; Uh oh
是我的自定义构造函数。
要查看new-range
如何处理等效项,我检查了其来源。它委托给LongRange
,ASeq
的{{1}}方法以下列行开头:
ASeq
由于我的范围未实现equiv
或public boolean equiv(Object obj) {
if (!(obj instanceof Sequential) && !(obj instanceof List)) {
return false;
. . .
,因此此检查失败。它甚至不尝试迭代我的范围以进行值比较。
这是什么原因? Sequential
只是一个空接口。似乎只存在“标记”类为顺序类而不需要任何方法。
我可以让我的范围实现List
允许检查,但是我想知道我的等效函数是否也应该包括与Sequential
相同的检查。不过,这似乎是不必要的检查,因为Sequential
在通过ASeq
处理错误的参数时已经失败。
进行seq
检查的目的是什么,我应该实施clojure.lang.RT/seqFrom
来安抚此类方法吗?在类似的方法中也应该进行此类检查吗?
答案 0 :(得分:1)
您想从中获得什么返回值
(= [1 2] #{1 2})
或来自
(= '([1 2]) {1 2})
?在这两种情况下,seq
之后,这两个集合是无法区分的(无论如何,这取决于#{1 2}
的散列方式)。但是它们显然不是相等的集合:映射和集合的行为与列表和向量的行为非常不同。区分每对的主要区别在于,其中一对是顺序的(旨在以顺序方式使用),而另一半则不是。这就是该标记界面的作用。
所以,是的,您应该在声明顺序对象与其他对象相等之前检查顺序对象:它不能合理地等于任何非顺序对象。