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
一般来说:
- 当你想testing两个数字是否相等时,使用
=
谓词。 - 使用
eqv?
当你想testing两个非数字值是否相等时谓词。 - 使用
equal?
当你想testing两个列表,向量等是否相等时,谓词。 - 不要使用
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的,细节在报告中。