3

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

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

4

3 回答 3

4

问题中的代码不起作用,因为在这一行中:

(set! list (cons value (cdr list)))

...您正在为list parameter分配一个新值,但这是过程本地的更改,它不会反映在p变量中的过程“外部”。您只是将list参数指向不同的列表,但p在过程返回后仍指向原始列表。

实际上,set-car!必须是原始操作。但不要害怕,在 Racket 中,还有许多其他set-mcar!用于可变对的操作,如果未绑定,您只需要 import 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 '())
于 2013-08-29T15:00:32.190 回答
2

还没有人建议使用宏,所以这里有一个适合您情况的解决方案:

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

因为宏又名句法形式直接转换您的源代码,而无需调用过程。

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
于 2013-08-29T21:06:16.330 回答
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“指向”的内容。

于 2013-08-29T20:01:59.443 回答