在Racket中,没有set-car!
函数,我想像这样实现它:
(define myset-car!
(lambda (list value)
(if (not (list? list))
#f
(set! list (cons value (cdr list))))))
但它不起作用,如果我像这样使用它,它就不能改变列表的值:
(define p (list 2 3 4))
(myset-car! p 'a)
p ; p still is (2 3 4)
谁能告诉我为什么?我的工作有什么问题?
答案 0 :(得分:4)
问题中的代码不起作用,因为在这一行:
(set! list (cons value (cdr list)))
...您正在为list
参数分配一个新值,但这是该过程的本地更改,它不会反映在该过程的“外部” p
变量。您只是将list
参数指向不同的列表,但是一旦过程返回,p
仍然指向原始列表。
实际上,set-car!
必须是原始操作。但是不要害怕,在Racket中, 是一个set-mcar!
以及一堆其他可变对的操作,如果不绑定,你只需要导入mpair
:
(require scheme/mpair)
并始终使用所有可变对操作。例如,以下是如何构建可变列表:
(define mlst (mcons 1 (mcons 2 (mcons 3 '()))))
或者:
(define mlst (mlist 1 2 3))
访问元素:
(mcar mlst)
=> 1
(mcdr mlst)
=> (mcons 2 (mcons 3 '()))
当然,要改变元素:
(set-mcar! mlst 0)
mlst
=> (mcons 0 (mcons 2 (mcons 3 '())))
(set-mcdr! mlst '())
mlst
=> (mcons 0 '())
答案 1 :(得分:3)
还没有人建议使用宏,所以这里有一个适合您案例的解决方案:
(define-syntax-rule (myset-car! lst val)
(if (not (list? lst))
lst
(set! lst (cons val (cdr lst)))))
(define p (list 2 3 4))
(myset-car! p 'a)
p
产量
'(a 3 4)
因为宏a.k.a.句法形式直接转换你的源代码而不调用过程。
Dr Racket的宏扩展产生(即你的源代码转换为)
(define-syntax-rule (myset-car! lst val)
(if (not (list? lst)) lst (set! lst (cons val (cdr lst)))))
(define p (list 2 3 4))
(if (not (list? p)) p (set! p (cons 'a (cdr p))))
p
答案 2 :(得分:0)
有很多方法可以满足您的需求。一种方法是一起避免可变操作。例如:
;; If 'list' is a list? then change it's first value to 'value'
;; otherwise return the list.
(define my-fancy-cons
(lambda (list value)
(if (not (list? list))
list
(cons value (cdr list)))))
(define p '(2 3 4))
(set! p (my-fancy-cons p 'a))
函数my-fancy-cons
不会破坏性地修改其list
参数。然后你可以做以下事情:
(define p '(2 3 4))
(define px (my-fancy-cons p 'a))
仍然保持p
不变。如果您需要更改它,请按照我在第一个代码段中的操作进行更改,使用set! p ...
至于原始问题,在lambda
内,参数list
是本地的,而set!
只会更改list
'指向'的内容。