新年快乐!
我正在努力进入球拍(我不是一个经验丰富的Lisper)。
现在我在 Racket 8.3 中遇到了以下内容:
#lang racket
> (define str1 "hello")
> (define str2 "hello")
> (eq? str1 str2)
#t
我以为 #f 像这里一样。在 Guile 和 Common Lisp 中,这被退回了。
据此,我可以重现以下行为符合预期:
> (set! str1 (string #h #e #l #l #o))
> (set! str2 (string #h #e #l #l #o))
> (eq? str1 str2)
#f
然而,我看不出原因。
是不是在 Racket 中只有函数 STRING 返回两个不同的对象,而文字字符串像符号一样是唯一的?因此,情商?行为正常,但文字字符串的处理方式不同?
你会这么好心地给我解释一下吗?
谢谢。
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"))
并且您编译该文件,它只是完全明确地未定义该函数是否返回 true 或 false:即使READ
为两个字符串返回不同的对象(我不确定是否需要这样做,但可能是),文件编译器当然被允许合并它们,因此只有一个文字字符串(当然,因此允许注意到该函数总是返回 true并简单地将其变成(defun foo () t)
)。
所以答案是:永远不要假设文字相似或不相同:如果你想要相似但不相同的对象,你需要使用一个被定义为创建新对象的函数自己制作它们。
是不是,在 Racket 中只有函数 STRING 返回两个不同的对象......
不仅功能string
。几乎每个字符串函数,如string-append
,都可以创建两个不同的对象:
(define a "a")
(eq? (string-append a a) (string-append a a)) ;=> #f
。文字字符串像符号一样是唯一的?因此,情商?行为正常,但文字字符串的处理方式不同?
对于球拍,是的,这在文档中得到了保证:
默认读取器生成的字符串常量(请参阅读取字符串)是不可变的,它们以读取语法模式暂留。
为了详细说明,Racket使用read-syntax
来读取您的程序,而read-syntax
中的字符串读取是暂存的。
(define a (syntax-e (read-syntax))) ; input "a"
(define b (syntax-e (read-syntax))) ; input "a"
(eq? a b) ;=> #t
该标准不想规定实现术语,而是列出了应该#t
的内容,应该#f
的内容以及eq?
的所有内容也总是eqv?
,反之亦然。他们写了除指针相等性的关键信息之外的所有内容。即。 当两个参数是同一个对象时,eq?
返回 #t,就像 Java 的==
一样。
在方案中,未指定(eq? "a" "a")
。#t
和#f
都是可接受的结果。这是报告中的例子之一。"a"
或'(a b c)
等文本值是不可变的。这是从引用到有关存储模型的信息的引用。
在同一报告中,尝试改变文字被认为是一个错误。对于如下所示string-set!
,如下所示:
(define (f) (make-string 3 #*))
(string-set! (f) 0 #?) ⇒ unspecified
因此,make-string
每次调用都会创建一个新字符串,您真的无法使用,因此第一个示例是浪费周期的OK方案代码。如果要将结果绑定到变量,然后执行此操作,则可以访问如下所示的结果:
(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")
。解释器可能总是返回#f
甚至编译的代码,但编译后的代码更有可能返回#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)
创建一个新字符串,并且由于str1
和str2
是分开创建的,它们是看起来相同的不同字符串。
知道,可以检查此类复合数据类型是否与equal?
相等,当两个对象被视为相同时,通常当它们看起来相同时,这些数据类型将返回#t
。因此:
(equal? str1 str2) ⇒ #t
我想,你可能对这篇论文感兴趣,Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same
以获得完整的(哲学)答案。