我是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)
我的问题是:
有没有办法制作原始列表的副本并且只能在其上工作,所以最后原件不能更改?
答案 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中设计和思考的好方法。