Reputation: 29
In Racket, there is not a set-car!
function, and I want to implement it like this:
(define myset-car!
(lambda (list value)
(if (not (list? list))
#f
(set! list (cons value (cdr list))))))
But it does not work, it can't change the value of a list if I use it like this:
(define p (list 2 3 4))
(myset-car! p 'a)
p ; p still is (2 3 4)
Can anyone tell me why? what's wrong with what I do?
Upvotes: 3
Views: 1241
Reputation: 18917
Nobody suggested macros yet, so here's a solution that would work for your case:
(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
yields
'(a 3 4)
since macros a.k.a. syntactic forms transform your source code directly without invoking procedures.
Macro expansion in Dr Racket yields (i.e. your source code gets transformed to)
(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
Upvotes: 2
Reputation: 70155
There are a number of ways to accomplish your need. One way is to avoid mutable operations all together. For example:
;; 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))
The function my-fancy-cons
does not destructively modify its list
argument. You then get to do things like:
(define p '(2 3 4))
(define px (my-fancy-cons p 'a))
and still have p
unchanged. If you need to change it, do as I did in the first snippet, use set! p ...
As to you original question, within your lambda
, the parameter list
is local and set!
only changes what list
'points' to.
Upvotes: 0
Reputation: 236004
The code in the question doesn't work because in this line:
(set! list (cons value (cdr list)))
... you're assigning a new value to the list
parameter, but that's a change local to the procedure, it won't be reflected "outside" the procedure in the p
variable. You're simply pointing the list
parameter to a different list, but p
is still pointing to the original list once the procedure returns.
In fact, set-car!
has to be a primitive operation. But fear not, in Racket there is a set-mcar!
and a bunch of other operations for mutable pairs, if unbound you just have to import mpair
:
(require scheme/mpair)
And consistently use all of the mutable pair operations. For instance, here's how to build a mutable list:
(define mlst (mcons 1 (mcons 2 (mcons 3 '()))))
Or alternatively:
(define mlst (mlist 1 2 3))
To access elements:
(mcar mlst)
=> 1
(mcdr mlst)
=> (mcons 2 (mcons 3 '()))
And of course, to mutate elements:
(set-mcar! mlst 0)
mlst
=> (mcons 0 (mcons 2 (mcons 3 '())))
(set-mcdr! mlst '())
mlst
=> (mcons 0 '())
Upvotes: 4