eq ?, eqv?,equal ?,和=在Scheme中有什么区别?

我想知道这些操作之间有什么不同。 我在堆栈溢出中看到了类似的问题,但是它们是关于Lisp的,并且没有三个操作符之间的比较。 所以,如果这已经被问到,请让我知道。

我在Scheme中编写不同types的命令,并得到以下输出:

(eq? 5 5) -->#t (eq? 2.5 2.5) -->#f (equal? 2.5 2.5) --> #t (= 2.5 2.5) --> #t 

有人可以解释为什么是这样吗?

我会逐步回答这个问题。 让我们从=等价谓词开始。 =谓词用于检查两个数字是否相等。 如果你只提供了一个数字,那么它会引发一个错误:

 (= 2 3) => #f (= 2.5 2.5) => #t (= '() '()) => error 

这个eq? 谓词用于检查其两个参数是否在内存中表示同一个对象。 例如:

 (define x '(2 3)) (define y '(2 3)) (eq? xy) => #f (define yx) (eq? xy) => #t 

但是请注意,内存中只有一个空列表'() (实际上空列表不存在于内存中,但指向内存位置0的指针被视为空列表)。 因此,比较空列表eq? 将始终返回#t (因为它们表示内存中的相同对象):

 (define x '()) (define y '()) (eq? xy) => #t 

现在取决于执行情况eq? 可能会或可能不会返回#t ,如数字,string等原始值。例如:

 (eq? 2 2) => depends upon the implementation (eq? "a" "a") => depends upon the implementation 

这是eqv? 谓词进入画面。 eqv?eq?完全一样eq? 谓词,除了总是返回相同的原始值#t 。 例如:

 (eqv? 2 2) => #t (eqv? "a" "a") => #t 

因此eqv?eq?的超集eq? 而对于大多数情况下,你应该使用eqv? 而不是eq?

最后我们来equal? 谓词。 equal? 谓词和eqv?完全一样eqv? 谓词,除了它也可以用来testing两个列表,vector等是否有相应的元素满足eqv? 谓词。 例如:

 (define x '(2 3)) (define y '(2 3)) (equal? xy) => #t (eqv? xy) => #f 

一般来说:

  1. 当你想testing两个数字是否相等时,使用=谓词。
  2. 使用eqv? 当你想testing两个非数字值是否相等时谓词。
  3. 使用equal? 当你想testing两个列表,向量等是否相等时,谓词。
  4. 不要使用eq? 谓词,除非你确切地知道你在做什么。

RNRS规范中有两个页面与eq?, eqv?, equal? and =相关eq?, eqv?, equal? and = eq?, eqv?, equal? and = 。 这是草案R7RS规范 。 一探究竟!

说明:

  • =比较数字,2.5和2.5在数字上相等。
  • equal? 数字减less到= ,2.5和2.5在数字上相等。
  • eq? 比较“指针”。 在您的计划实施中,数字5被实现为“立即”(可能),因此5和5是相同的。 数字2.5可能需要在您的Scheme实现中分配一个“浮点logging”,这两个指针并不相同。

eq? 当它是相同的地址/对象时是#t通常可以期望#t为相同的符号,布尔值和对象,而#f为不同types,不同值或者不同结构的值 。Scheme / Lisp实现有一个embeddedtypes指针和embedded的传统值在同一个空间,如果它有足够的空间。 因此,一些指针实际上不是地址,而是像char R或Fixnum 10那样的值。 这些将是eq? 因为“地址”是一个embedded式的types+值。 一些实现也重用了不可变的常量。 (eq?'(1 2 3)'(1 2 3))在解释时可能是#f,但编译时可能是#t,因为它可能会得到相同的地址。 (就像Java中的常量string池)。 正因为如此,涉及到eq?许多expressioneq? 是未指定的,因此它评估为#t或#f是依赖于实现的。

eqv? eq?是一样的eq? 它也是#t,如果它是一个数字或字符,并且它的值是相同的 ,即使数据太大而不适合指针。 那对于那些eqv? 检查该types的额外工作是否是支持的types之一,它们是相同的types,并且目标对象具有相同的数据值。

equal? 是否和eqv? 如果它是一个像pair,vector,string和bytevector这样的复合types,它recursion地equal? 与零件。 在实践中,如果两个对象看起来相同,它将返回#t 。 在R6RS之前,使用equal?是不安全的equal? 在圆形结构上。

=就像eqv?它只适用于数字types 。 这可能会更有效率。

string=? 就像equal? ,但它只适用于string。 这可能会更有效率。

equal? recursion地比较两个对象(任何types)是否相等。

  • 请注意,对于大型数据结构来说,这可能是很昂贵的,因为必须遍历整个列表,string,向量等。

  • 如果对象只包含一个元素(EG:数字,字符等),这和eqv?是一样的eqv?


eqv? testing两个对象以确定两者是否“通常被视为同一对象”。

  • eqv?eq? 是非常相似的操作,它们之间的差异将有一些实现特定的。

eq?eqv? 但可能能够辨别更细微的区别,并且可以更有效地实施。

  • 根据规范,这可能被实现为一个快速和有效的指针比较,而不是一个更复杂的eqv?操作eqv?

=比较数字相等的数字。

  • 请注意,可以提供两个以上的数字,例如: (= 1 1.0 1/1 2/2)

你没有提到一个scheme的实施,但在Racket, eq? 只有当参数引用同一个对象时才返回true。 你的第二个例子是产生#f,因为系统为每个参数创build一个新的浮点数; 他们不是同一个对象。

equal?=正在检查值相等,但=只适用于数字。

如果您使用的是Racket,请点击这里查看更多信息。 否则,请检查您的计划实施的文件。

想想eq? 作为指针平等。 报告的作者希望它尽可能通用,所以他们不要直接这么说,因为它是依赖于实现的,并且说它会偏向基于指针的实现。 但他们确实这样说

通常可以实现eq? 比eqv更有效率,例如,作为一个简单的指针比较

这是我的意思。 (eqv? 2 2)保证返回#t但是(eq? 2 2)是未指定的。 现在想象一下基于指针的实现。 在这方面eq? 只是指针比较。 由于(eq? 2 2)被指定,这意味着这个实现可以自由地为它从源代码中读取的每个新数字创build新的内存对象表示。 eqv? 必须真正检查它的论点。

OTOH (eq 'a 'a) eq'a'a (eq 'a 'a)#t 。 这意味着这种实现必须识别具有重复名称的符号,并在内存中为它们全部使用同一个表示对象。

假设一个实现不是基于指针的。 只要坚持报告,就没有关系。 作者只是不希望被视为执行者的具体实施,所以他们仔细select他们的措辞。

无论如何,这是我的猜测。

所以非常粗糙, eq? 是指针相等, eqv? 是(primefaces)价值感知, equal? 也是结构感知的(recursion地检查它的参数,所以最后(equal? '(a) '(a))被要求为#t ), =是对于数字, string=? 是用于string的,细节在报告中。