Scheme中列表(或其他)的副本

时间:2013-12-27 14:45:53

标签: list copy scheme racket

我是Scheme的新手,发现如果我用set-car更改列表!/ set-cdr! (甚至在本地)父类列表也被修改。这就是我的意思:

(define my-list '(1 2 3 4 5)) ; Original list

(define (function list) ; Some example function
   (let ((copy list))
        (set-car! copy 'new)
   (display copy)
)

(function my-list); will display (new 2 3 4 5)

my-list; but the original is changed "forever" and will be also '(new 2 3 4 5)


我的问题是: 有没有办法制作原始列表的副本并且只能在其上工作,所以最后原件不能更改?

2 个答案:

答案 0 :(得分:9)

您的代码(let ((copy list))允许通过名称副本访问列表。因此,当您set-car!复制时,您实际上是set-car!原始列表。

lisp语言中的突变现象起初有点令人困惑。

由于您发现的原因,通常应避免突变。列表和事物的一部分浮动。这是因为列表中的每个节点都有两个部分 - car是它的值可能指向另一个列表,而cdr是跟随它的部分 - 通常这是一个点亮的东西类似于car中的值。

这解决了使用SRFI1的列表复制功能的问题     (让((复制(列表 - 复制清单)))

这是复制列表的一种方法(制作新列表)。这段代码并不完整。当我们在内部查看它时,它会在每个递归调用中添加一个列表的片段。每个新列表部分的部分来自哪里? cdr是通过调用(list-copy (cdr list))生成的,而car只是被采用 - 而不是被复制! - 从另一个清单。

(define (list-copy list)
  (if (null? list) '() (cons (car list) (list-copy (cdr list)))))

您在进行实验时得到的结果是,copy借用的my-list列表中的汽车。

这是一个更合适的版本:

(define (full-copy list)
(if (null? list) 
  '() 
  (if (list? list) 
      (cons (full-copy (car list)) (full-copy (cdr list)))
      list)))

只有car的代码发生了变化。现在,汽车被重建为。借用的唯一部分是数字(因此,当数字更为特殊时,此副本不合适)。我不确定srfi1版本是如何工作的。

let语句只会将一个标识符绑定到一个对象 - 该对象不会被复制,您只需要另一种方法来访问它。所以任何变化(使用变异,如在以“!”结尾的函数中)都将影响两者。

答案 1 :(得分:4)

我建议您在学习Scheme或Racket时,避免使用!set-car!set-cdr!等所有set!函数。

不要改变事物,而是要考虑从旧事物中创造新事物。

例如,如果您有一个列表并且在开始时想要一些新内容:

;; Return a new list with `x` replacing the head of the list `xs`.
(define (replace-head xs x) ; list? any? -> list?
  (cons x (cdr xs)))

;; Using it:
(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)

P.S。可以处理replace-head为空的xs版本可以是:

(define (replace-head xs x) ; list? any? -> list?
  (cond [(empty? xs) (list x)]
        [else        (cons x (cdr xs))]))

(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)
(replace-head '() 100)
; => '(100)

您也可能会发现查看How to Design Programs有助于了解如何在Scheme或Racket中设计和思考的好方法。