我正在编写Scheme中不同类型的命令,并获得以下输出:
(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t
为什么会这样?
(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
< p > eq?
谓词用于检查其两个参数是否在内存中表示相同的对象。例如:
(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
一般而言:
=
谓词。eqv?
谓词。equal?
谓词。eq?
谓词。在RnRS规范中,有两整页的内容涉及eq?,eqv?,equal?和=
。 这是R7RS规范草案,请查看!
说明:
=
比较数字,2.5和2.5在数值上相等。equal?
对于数字降为=
,2.5和2.5在数值上相等。eq?
比较“指针”。在您的Scheme实现中,数字5作为“立即数”(可能)实现,因此5和5是相同的。数字2.5可能需要在您的Scheme实现中分配'浮点记录'来存储,这两个指针不相同。equal?
/eqv?
的评论在某种程度上是不正确的。equal?
/eqv?
会在两个操作数具有相同的精确度时缩减为=
。所以(= 1 1.0)
是真的,而(eqv? 1 1.0)
是假的。 - undefinedeq?
是当地址/对象相同时为#t
。一般来说,我们可以期望对于相同的符号、布尔值和对象,以及不同类型、不同值或不同结构的值,返回#f
。Scheme/Lisp实现有将类型嵌入指针并在相同空间内嵌入值的传统。因此,某些指针实际上不是地址而是值,例如字符R
或Fixnum 10
。由于“地址”是嵌入的类型+值,因此这些将是eq?
。一些实现还会重用不可变常量。当解释时,(eq? '(1 2 3) '(1 2 3))
可能为#f
,但编译后可能为#t
,因为它可能获得相同的地址(类似于Java中的常量字符串池)。由于这个原因,许多涉及eq?
的表达式是未指定的,因此它是否评估为#t
或#f
取决于实现。
eqv?
对于与eq?
相同的内容为#t
。如果它是数字或字符且其值相同,则也为#t
,即使数据太大而无法适应指针。因此,对于这些内容,eqv?
会额外检查类型是否受支持,两者是否是相同类型以及其目标对象是否具有相同的数据值。
equal?
对于与eqv?
相同的内容为#t
,如果它是一种复合类型(例如pair、vector、string和bytevector),则会递归执行equal?
操作。在实践中,如果两个对象看起来相同,则返回#t
。在R6RS之前,在循环结构上使用equal?
是不安全的。
=
类似于eqv?
,但它只适用于数字类型。它可能更有效率。
string=?
类似于equal?
,但它只适用于字符串。它可能更有效率。
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 Nesseq?
和eqv?
之间的差异比其他操作更微妙。 - Justin Ethiereq?
仅在参数指向相同对象时返回true。您的第二个示例返回#f是因为系统为每个参数创建了一个新的浮点数;它们不是同一个对象。
equal?
和=
用于检查值的等价性,但=
只适用于数字。eq?
视为指针相等。报告的作者希望它尽可能通用,因此他们没有明确说明这一点,因为它取决于实现,并且说出来会支持基于指针的实现。但是,他们确实说了:(eqv? 2 2)
保证返回#t
,但(eq? 2 2)
是未指定的。现在想象一个基于指针的实现。在其中,eq?
只是指针比较。由于(eq? 2 2)
是未指定的,因此这意味着该实现可以自由地创建每个从源代码中读取的新数字的新内存对象表示。而eqv?
必须实际检查其参数。(eq 'a 'a)
是#t
。这意味着这样的实现必须识别具有重复名称的符号,并在内存中为所有符号使用相同的一个表示对象。eq?
是指针相等性,eqv?
是(原子)值感知的,equal?
也是结构感知的(递归检查其参数,以便最终(equal? '(a) '(a))
需要是#t
),=
用于数字,string=?
用于字符串,详细信息请参见报告。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.
eqv?
的函数,它的意思与eq?
或equal?
不同。 - newacct