eq?在 Racket 中表现与 eq 不同?在 Guile 中还是在 Common Lisp 中是 eq?

Does eq? in Racket behave different from eq? in Guile or eq in Common Lisp?

新年快乐!

我正在努力学习 Racket(我不是经验丰富的 Lisper)。

现在我在 Racket 8.3 中遇到了以下内容:

#lang racket
> (define str1 "hello")
> (define str2 "hello")
> (eq? str1 str2)
#t

我预计 #f 会像 here。在 Guile 和 Common Lisp 中,这被返回了。

根据 this 我可以重现以下行为符合预期:

> (set! str1 (string #\h #\e #\l #\l #\o))
> (set! str2 (string #\h #\e #\l #\l #\o))
> (eq? str1 str2)
#f

但我看不出原因。

是不是,在 Racket 中只有函数 STRING returns 两个不同的对象,而文字字符串像符号一样是唯一的?因此,当?行为通常但文字字符串的处理方式不同?

你能不能好心给我解释一下?

非常感谢。

eq? 所做的与 eq 在 Common Lisp 中所做的相同:它告诉您两个对象是否是同一个对象。

您看到的是,通常没有定义 相似的文字对象 实际上是否是同一个对象,将它们设为相同的。一个特别的好处是您可以使用更少的内存。

在 Racket 的情况下,文字字符串显然是这样合并的,至少在某些情况下是这样。 (但并非总是如此:给定以下程序

#lang racket

(define (ts)
  (define (getit prompt)
    (printf "~S? " prompt)
    (read))
  (eq? (getit "1")
       (getit "2")))

(ts)

然后

$ racket ts.rkt
"1"? "foo"
"2"? "foo"
#f

例如。)

一般来说,你不应该假设相似的字面值实际上是否是同一个对象,因为系统可能会也可能不会选择合并它们,也可能会或可能不会选择做所以在任何特定情况下。

举个例子,在 CL 中如果你有一个包含这个函数的文件

(defun foo ()
  (eq "foo" "foo"))

然后你编译那个文件,它只是完全明确地未定义函数 returns 是真还是假:即使 READ returns 两个字符串的不同对象(我我不确定是否需要这样做,但它可能是),当然允许文件编译器合并它们,因此只有一个文字字符串(当然因此允许注意到函数总是 returns true 并简单地把它变成 (defun foo () t)).

所以答案是:永远不要假设相似的文字是相同的还是不相同的:如果你想要相似但不相同的对象,你需要自己用一个定义为的函数来制作它们制作新对象。

Is it, that in Racket only the function STRING returns two different objects ...

函数string。几乎每个字符串函数,如 string-append,都可以创建两个不同的对象:

(define a "a")
(eq? (string-append a a) (string-append a a)) ;=> #f

... a literal string is unique like a symbol? Thus, eq? behaves usually yet literal strings are treated differently?

对于 Racket,是的,这在 documentation:

中得到保证

string constants generated by the default reader (see Reading Strings) are immutable, and they are interned in read-syntax mode.

更详细一点,Racket 使用 read-syntax 来读取你的程序,并且 read-syntax 中的字符串读取是 interned。

(define a (syntax-e (read-syntax))) ; input "a"
(define b (syntax-e (read-syntax))) ; input "a"
(eq? a b) ;=> #t

我认为,您可能会对论文 Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same 感兴趣以获得完整(哲学)答案。

该标准不想规定实施行话,而是列出了应该 #t、应该 #f 以及一切 eq? 始终也是eqv?,反之则不然。除了指针相等性这一关键信息外,他们写下了所有内容。 IE。 eq? returns #t 当两个参数是同一个对象时,如 Java 的 ==

在 Scheme 中,(eq? "a" "a") 未指定。 #t#f 都是可以接受的结果。它是 examples in the report. Literal values like "a" or '(a b c) are immutable. This is referenced from quote to the information about the storage model.

之一

在同一份报告中,试图改变文字被认为是错误的。 string-set! 显示如下:

(define (f) (make-string 3 #\*))
(string-set! (f) 0 #\?)  ‌⇒  unspecified

所以 make-string 每次调用都会生成一个新的字符串,您确实不能使用它,因此第一个示例是浪费周期的 OK Scheme 代码。如果您要将结果绑定到一个变量然后执行此操作,您将可以像这样访问结果:

(define (f) (make-string 3 #\*))
(define test (f))
(string-set! test 0 #\?)  ‌⇒  unspecified
test  ‌                    ⇒  "?**"

现在第二个例子更切合主题。

(define (g) "***")
(string-set! (g) 0 #\?)  ‌⇒  unspecified
             ; should raise  &assertion exception

R5RS 甚至说它甚至不是 Scheme,所以任何结果都可以,而 R6RS 和后来强烈建议尝试变异应该引发异常。

对于那些调用 (g) 的人来说,大多数实现并非如此,当非法代码正常运行时,很可能会导致 "?**""***" 之一。

做回你的代码:

(define str1 "hello")
(define str2 "hello")
(eq? str1 str2) ⇒  unspecified

出于完全相同的原因,(eq? "a" "a") 未指定。解释器可能总是 return #f 甚至编译代码,但编译代码更有可能 return #t.

(define str1 (string #\h #\e #\l #\l #\o))
(define str2 (string #\h #\e #\l #\l #\o))
(eq? str1 str2)  ⇒  #f

它们总是不同的,因为 (string #\h #\e #\l #\l #\o) 创建了一个新字符串,并且由于 str1str2 是分别创建的,所以它们是不同的字符串,但看起来相同。

知道可以使用 equal? 检查此类复合数据类型是否相等,当两个对象被认为相同时,return #t,例如。通常当它们看起来相同时。因此:

(equal? str1 str2)  ⇒  #t