Scheme中的eq?,eqv?,equal?和=之间有什么区别?


85

我想知道Scheme中的那些操作之间有什么区别。我在Stack Overflow中看到了类似的问题,但它们与Lisp有关,并且其中三个运算符之间没有比较。

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

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

为什么会这样呢?


3
并且还有eqv?,这意味着与eq?或不同equal?
newacct

Answers:


155

我将逐步回答这个问题。让我们从=等价谓词开始。该=谓词用于检查两个数字是否相等。如果您提供除数字以外的其他任何内容,则会引发错误:

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

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

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

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

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

现在,根据实现的不同,eq?可能会也可能不会返回#t原始值(例如数字,字符串等)。例如:

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

这是eqv?谓词出现的地方。的eqv?是完全一样的eq?断言,但它总是返回#t了相同的原始值。例如:

(eqv? 2 2)     => #t
(eqv? "a" "a") => depends upon the implementation

因此eqv?是的超集,eq?在大多数情况下,您应使用eqv?而不是eq?

最后我们来到equal?谓词上。的equal?谓词是完全一样的eqv?谓词,除了它也可以被用来测试是否两个列表,载体等具有对应的满足元素eqv?谓词。例如:

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

一般来说:

  1. =当您希望测试两个数字是否相等时,请使用谓词。
  2. eqv?当您希望测试两个非数字值是否相等时,请使用谓词。
  3. equal?当您希望测试两个列表,向量等是否相等时,请使用谓词。
  4. eq?除非您确切地知道自己在做什么,否则不要使用谓词。

7
AFAIK (eqv? "a" "a") ==> unspecified。您必须使用equal?或(可能是经过优化的)string=?
Sylwester

3
根据该报告(eq? '(1) '(1))未指定的,所以你的(define x '(1 2))插图可能无法正常工作。
尼斯,

4
非常准确和翔实。尤其是最后的指南。
赫尔曼DIAGO

2
但是eq?似乎是为符号定义的,应该注意!如果符号看起来相同,是否等于?返回#t。例如(eq? 'foo 'foo) -> #t(eq? 'foo 'bar)-> false`。我在这里这里
Nedko '16

13

RnRS规范中有与之相关的整整两页eq?, eqv?, equal? and =。这是R7RS规范草案。一探究竟!

说明:

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

1
R7RS规范草案的链接自
2018年

2
更新为实时链接。
GoZoner

10

eq?#t相同的地址/对象。通常,人们可能期望#t表示相同的符号,布尔值和对象,而#f表示不同类型,具有不同值或结构不相同的值。Scheme / Lisp实现具有将类型嵌入其指针并嵌入的传统。如果有足够的空间,则将值放在同一空间中。因此,某些指针实际上不是地址而是值,例如charR或Fixnum 10。这是eq?因为“地址”是嵌入式类型+值。一些实现还重用不可变常量。(eq?'(1 2 3)'(1 2 3))在解释时可能是#f,在编译时可能是#t,因为它可能获得相同的地址。(就像Java中的常量字符串池一样)。因此,许多表达涉及eq? 是未指定的,因此计算得出的结果是#t或#f取决于实现。

eqv?#t与的含义相同eq?。如果它是数字或字符并且其值相同,即使数据太大而无法容纳指针,它也是#t。因此,对于这些eqv?类型的检查,需要做额外的工作,以检查类型是否受支持,两者都是相同的类型,并且目标对象具有相同的数据值。

equal?与#t相同eqv?,如果它是对,向量,字符串和字节向量之类的复合类型,则它是递归地equal?处理各部分。实际上,如果两个对象看起来相同,它将返回#t。在R6RS之前,equal?在圆形结构上使用是不安全的。

=就像,eqv?仅适用于数字类型。可能会更有效率。

string=?就像equal?,但仅适用于字符串。可能会更有效率。


6

equal? 递归比较两个对象(任何类型)是否相等。

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

  • 如果对象仅包含单个元素(例如:数字,字符等),则与相同eqv?


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

  • eqv?并且eq?是非常相似的操作,并且它们之间的差异将在某种程度上是特定于实现的。

eq?与相同,eqv?但可以辨别出更好的区别,并且可以更有效地实现。

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


= 比较数字的数值相等性。

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

我以为eq?是实际的指针相等(不是eqv?)。这是“最好的或最有区别的”。例如,(eqv? 2 2)可以保证是#t,但是(eq? 2 2)“未指定”。也就是说,这取决于实现是否为每个新读取的数字创建实际的新存储对象,或者是否可以重用先前创建的对象。
Will Ness

@WillNess-很好,谢谢。eq?和之间的差异eqv?比其他操作更细微。
贾斯汀·埃斯蒂尔

5

您没有提及方案实现,但是在Racket中,eq?仅当参数引用相同的对象时才返回true。您的第二个示例产生#f,因为系统正在为每个参数创建一个新的浮点数。他们不是同一个对象。

equal?并且=正在检查值是否相等,但=仅适用于数字。

如果您使用的是球拍,请在此处查看更多信息。否则,请检查方案实施的文档。



3

可以认为eq?是指针相等。该报告的作者希望它尽可能通用,因此不要直接说出来,因为它是依赖于实现的,并且更倾向于基于指针的实现。但是他们确实说

通常可以实现eq?比eqv?有效得多,例如,作为简单的指针比较

这就是我的意思。(eqv? 2 2)保证返回,#t但未(eq? 2 2)指定。现在想象一下基于指针的实现。其中eq?只是指针比较。由于(eq? 2 2)未指定,这意味着该实现可以自由地创建它从源代码读取的每个新数字的新存储对象表示形式。eqv?必须实际检查其参数。

OTOH(eq 'a 'a)#t。这意味着,这样的实现必须认识符号重名并使用相同的一个表现对象在内存中所有的人。

假设一个实现不是基于指针的。只要遵守报告,就没有关系。作者只是不想被看作是向实现者指示实现的细节,因此他们会仔细选择措辞。

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

因此,粗略地说,eq?是指针相等,eqv?是(原子)值感知的,equal?还是结构感知的(递归地检查其参数,因此最终(equal? '(a) '(a))必须是#t),=是数字,string=?字符串和细节。在报告中。


0

除了先前的答案,我还将添加一些评论。

所有这些谓词都想定义的抽象功能 identity对象但要在不同的上下文中。

EQ?与实现有关,并且are 2 objects the same?仅在有限的使用中不能回答问题。从实现的角度来看,此谓词仅比较2个数字(指向对象的指针),而不查看对象的内容。因此,例如,如果您的实现不是唯一地将字符串保留在内部,而是为每个字符串分配了不同的内存,那么(eq? "a" "a")它将为false。

EQV?-它看起来在对象内部,但用途有限。如果为返回true,则取决于实现(eqv? (lambda(x) x) (lambda(x) x))。在这里,如何定义此谓词是一种完整的哲学,因为我们如今知道,有一些快速方法可以比较某些功能的功能,而使用范围有限。但是eqv?为大数字,字符串等提供了连贯的答案。

实际上,其中一些谓词尝试使用对象的抽象定义(数学上),而其他谓词则使用对象的表示形式(如何在真实机器上实现)。身份的数学定义来自莱布尼兹,它说:

X = Y  iff  for any P, P(X) = P(Y)
X, Y being objects and
P being any property associated with object X and Y.

理想情况下,应该能够在计算机上实现此定义,但是出于不确定性和/或速度的原因,它不是从字面上实现的。这就是为什么有许多运营商尝试每个运营商专注于围绕此定义的不同观点的原因。

尝试想象一个延续的身份的抽象定义。即使您可以提供函数子集的定义(sigma-递归函数类),该语言也不会强加任何谓语为true或false。语言的定义和实现的复杂性将大大增加。

其他谓词的上下文更易于分析。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.