为什么集车!像这样的工具不起作用

时间:2013-08-29 14:58:26

标签: scheme racket

在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)

谁能告诉我为什么?我的工作有什么问题?

3 个答案:

答案 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'指向'的内容。