为什么 "eq?" 在以下上下文中评估为 false 而在其他情况下评估为 true?

Why does "eq?" evaluate to false in the following context but true otherwise?

我目前正在练习球拍语言,我运行遇到了一个有趣的问题。我正在尝试比较两个列表的元素。通常,如果我比较两个符号,我会得到以下结果:

> (eq? 'leet 'leet)
#t
> (eq? 'let 'notleet)
#f

出于某种原因,当比较两个列表的第一个元素时,即使它们相等,我也会得到 false。

> (eq? (first '('leet 'a 'f)) (first '('leet 'coder 'a 'f 'f)))
#f

当我基本上比较相同的两件事时,为什么会评估为 false?

when I'm basically comparing the same two things

你不是。 (first '('leet 'a 'f))'(quote leet),而不是 ‘leet。所以你比较的是列表,而不是符号。

'(...) 已经引用了列表的内容。如果您在列表中放置额外的 's,它们将被引用。由于 'foo(quote foo) 的快捷方式,引用它可以得到包含这些符号的列表。

如果你只写 '(leet a f) 而没有内引号,它会按你预期的那样工作。

表达式'expression(quote expression)的缩写。计算表达式时,它的计算结果为 expression 作为数据结构或原子值。重要的是要知道 expression 中的任何内容都不会得到进一步评估。因此 ''x,即 (quote (quote x)) 成为列表 (quote x)

eq?用于比较同一个对象。这意味着:

(eq? (list 'leet) (list 'leet)) ; ==> #f

现在两个参数看起来都像 (leet),但是这两个列表位于计算机的不同内存位置,因此不相同。

"string"'(some list) 这样的常量可能会被创建一次然后被多次引用,但在不同的实现中可能会为代码中的每个位置新创建常量。因此:

(eq? "test" "test")   ; ==> #t or #f
(eq? '(leet) '(leet)) ; ==> #t or #f

在你的代码中你有多余的 ' 所以 (first '('leet 'a 'f)) 实际上是数据 (quote leet),一个有两个符号的列表。因此,您正在应用与上面最后一个表达式完全相同的内容,并且您可以期望某些实现中的 #f 和其他一些实现中的 #t 。比较列表与比较符号不同。

因此您可以通过删除多余的 ' 来解决此问题。那么我假设您没有尝试列出列表 (quote leet)

(eq? (first '(leet a f)) (first '(leet coder a f f)))
; ==> #t

如果你想比较列表,你应该使用 equal?:

(equal? (first '('leet 'a 'f)) (first '('leet 'coder 'a 'f 'f)))
; ==> #t

并且知道 #lang racket 中的 (first '('leet 'a 'f)) REPL 用两个 ' 打印 ''leet。第一个 ' 是球拍有趣的打印值的方式,它计算出它应该打印的值,也许是这种混淆的根源,第二个是你有一个列表 (quote leet) 的指示符,但是许多方案将其缩写为 'leet.

Sylwester 的回答是正确和详细的,但我想提出 TL/DR;这里:

不要使用 eq?。相反,使用 equal?.

这就是故事的全部吗?不,当然不。但是,如果您正在寻找可以牢记在心的单行本,那应该是这个; equal? 几乎总能如愿以偿,而 eq? 通常不会。