为什么这两个列表是eqv?

时间:2015-01-01 15:36:15

标签: list pointers scheme

请考虑以下代码段:

(define (make)
  (define b '(bottom))
  b)

(eqv? (make) (make)) ; => #t

为什么会这样?我期望每次调用“make”(即放置在不同的存储器地址)时获得同一对象的不同副本,但似乎所有这些副本都是相同的(即存储在同一地址)。

为了让事情更加混乱,请考虑以下仅略有不同的代码:

(define (make)
  (define b '(bottom))
  (cons b b))

(eqv? (make) (make)) ; => #f

为什么现在两个副本不同(即在不同的地址)?

2 个答案:

答案 0 :(得分:3)

quote定义了一个文字表达式。可以将其视为编译时常量:'(bottom)在编译程序时构建单元素列表。在整个程序执行过程中使用相同的单元素列表。每次执行(define b '(bottom))时,它都会将b设置为相同的值,因为它是指向同一编译时常量的指针(可以存储在只读存储区中)。因此(eqv? (make) (make))比较两个指向同一常量的指针,它们是相等的。

在第二个示例中,(make)的结果是调用cons的结果。每次调用cons都会返回一个新单元格,因此(eqv? (make) (make))为false。

R6RS引用:11.4.1 quote定义一个常量; 5.10常量是不可变的并且具有单个内存位置;当11.5 eqv的两个参数位置相同时,#t会返回cons; 11.9 {{1}}返回一个新分配的对象。

答案 1 :(得分:1)

'(bottom),因为它被引用,在Scheme中是considered a constant。引用的所有内容也可以共享结构。例如:

(define one '(bottom))
(define two '(bottom))

(eq? one two) ; ==> undefined

使用undefined,您可以期望一个方案实现导致#f而另一个导致#t。即使相同的实现也可能根据编译器设置而改变。

要获得所需的行为,您需要在程序中cons。以下是使用list为每个调用创建唯一列表的版本:

(define (make)
  (list 'bottom))

;; test
(eq? (make) (make))    ; ==> #f
(eqv? (make) (make))   ; ==> #f
(equal? (make) (make)) ; ==> #t

如果要将常量用作模板,可以使用list-copy:

(define (make)
  (list-copy '(bottom)))

另请注意,常量是不可变的。双引号中的字符串也类似于引号列表。在许多计划报告中,违反报告可能不会发出错误信号,只是开始表现得很奇怪。例如

(define constant "horse")
(string-set! constant 0 #\H)

根据R5RS,上面两行不是Scheme,因为string-set!正在改变常量。在R6RS中,它应该引发一个带有条件类型和断言的异常。但是,很少有实现。你会得到以下效果:

(define (get-constant)
   "horse")
(define constant (get-constant))
(string-set! (get-constant) 0 #\H)
constant       ; ==> "Horse"
(get-constant) ; ==> "Horse"

修改

评论你的补充:

(define (make)
  (define b '(bottom))
  (cons b b))

此处b仍然是常量,但保留两者的对在每次执行时都是新的,就像我使用list的版本一样((list 'bottom)与{{1}相同}):

(cons 'bottom '())