This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

How to reset the value of associate list element

10 posts · 10736 views

If I have a associate list ((:a a) (:b b) (:c c)), how can I set b to (:b z)?

Can you tell me different ways to do it?

Thanks!!

Re: How to reset the value of associate list element

joybee wrote:If I have a associate list ((:a a) (:b b) (:c c)), how can I set b to (:b z)?

Can you tell me different ways to do it?

Thanks!!
Not knowing the answer myself, I figured out the easy way.
(let ((lst '((:a a) (:b b) (:c c))))
           (setf (car (cdr (car (cdr lst)))) 'z) ;abra-cadadr!
           lst)
I thought there was something like "getf" that could be passed as a place to setf, and which would obviate the need for the car-cdr'ing. I should check the docs.

Edit: Also, SBCL is yelling at me about "constant data" for some reason.

Edit again: This is fine, oddly enough:
(let ((lst (list '(a a) '(b b) '(c c))))
           (rplacd (assoc 'b lst) 'z))
Yet the list form and the quoted form evaluate to the same thing. (Whether I use car/cdr or rplacd has no effect, incidentally.)
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: How to reset the value of associate list element

(car (cdr (car (cdr <list>)))) => (cadadr <list>)
The standard way to replace a value in an a-list is the same like Duke already had written above:
(rplacd (assoc <key> <a-list>) <new-value>)
This is in so far the better solution as you normally do not know the exact position of the (key value) pair inside the a-list. 'assoc' automatically finds the right position and 'rplacd' replaces the value field.

Re: How to reset the value of associate list element

Duke wrote:Yet the list form and the quoted form evaluate to the same thing.
Not quite. LIST is a function which creates a list at runtime, whereas QUOTE is a special operator which creates a list at compile time. Quoted forms are treated as source literals, and modifying them has an unspecified behaviour, which in principle might include a memory protection fault if they are placed in read-only memory. I don't think any implementation actually does that, but modifying literals is usually not a good idea.

Re: How to reset the value of associate list element

Thanks, everyone!

Here is a little example I ran on sbcl.

* (equal (list 1 2) (rplacd (list 1 1) 2))
* NIL

* (rplacd (list 1 1) 2)
* (1 . 2)

What I want is after value reset the evaluation of equal returns T. How can I achive that?

Re: How to reset the value of associate list element

Ramarren wrote:Not quite. LIST is a function which creates a list at runtime, whereas QUOTE is a special operator which creates a list at compile time. Quoted forms are treated as source literals, and modifying them has an unspecified behaviour, which in principle might include a memory protection fault if they are placed in read-only memory. I don't think any implementation actually does that, but modifying literals is usually not a good idea.
I see. I guess I should have read the documentation on QUOTE while I was at it. ;)
Thanks.
joybee wrote:Thanks, everyone!

Here is a little example I ran on sbcl.

* (equal (list 1 2) (rplacd (list 1 1) 2))
* NIL

* (rplacd (list 1 1) 2)
* (1 . 2)

What I want is after value reset the evaluation of equal returns T. How can I achive that?
You notice that rplacd returns a dotted pair? That's a cons, which is distinct from a list. Putting it simply, a list is a set of conses with a NIL on the end, like this:
(rplacd (list 1 1) 2) => (1 . 2)
(list 1 2) => (1 . (2 . nil))
So you see why EQUAL returns NIL in your example. Try these:
(equal (cons 1 (cons 2 nil)) (list 1 2)) => T
(equal (cons 1 2) (rplacd (list 1 1) 2)) => T
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: How to reset the value of associate list element

Duke wrote:
Ramarren wrote:Not quite. LIST is a function which creates a list at runtime, whereas QUOTE is a special operator which creates a list at compile time. Quoted forms are treated as source literals, and modifying them has an unspecified behaviour, which in principle might include a memory protection fault if they are placed in read-only memory. I don't think any implementation actually does that, but modifying literals is usually not a good idea.
I see. I guess I should have read the documentation on QUOTE while I was at it. ;)
Thanks.
joybee wrote:Thanks, everyone!

Here is a little example I ran on sbcl.

* (equal (list 1 2) (rplacd (list 1 1) 2))
* NIL

* (rplacd (list 1 1) 2)
* (1 . 2)

What I want is after value reset the evaluation of equal returns T. How can I achive that?
You notice that rplacd returns a dotted pair? That's a cons, which is distinct from a list. Putting it simply, a list is a set of conses with a NIL on the end, like this:
(rplacd (list 1 1) 2) => (1 . 2)
(list 1 2) => (1 . (2 . nil))
Though it is represented as (1 2). You can see the same thing by consing together some objects with NIL:
(cons 1 (cons 2 nil))
Hopefully you see why EQUAL returns NIL in your example. Try this:
(equal (cons 1 2) (rplacd (list 1 1) 2)) => T
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: How to reset the value of associate list element

The particular problem here is that an association list usually is a list of dotted pairs like this:
((:a . a) (:b . b) (:c . c))
This means that the last element in each cell is a symbol and not NIL, like in a normal (non-dotted) list. 'assoc' works with normal list as well as with dotted lists, but 'rplaca' expects a cons (a dotted pair) and not a normal list.

If you want to use a normal list instead of a dotted list as an association list (what is not a really good idea btw.), then there is no other way than to use 'setf' together with 'second' to replace the second element in a cell like this:
(let ((lst (list (list :a 'a) (list :b 'b) (list :c 'c))))
  (setf (second (assoc :b lst)) 'z)
  lst)
=> ((:A A) (:B Z) (:C C))
But using normal lists as association list is quite non-standard Common Lisp.

You can create a dotted association list by using 'cons' like this:
(list (cons :a 'a) (cons :b 'b) (cons :c 'c))
=> ((:A . A) (:B . B) (:C . C))
Now also all the code from above works:
(let ((lst (list (cons :a 'a) (cons :b 'b) (cons :c 'c))))
  (rplacd (assoc :b lst) 'z)
  lst)
=> ((:A . A) (:B . Z) (:C . C))

(equal (cons 1 2) (rplacd (cons 1 1) 2))
=> T

(rplacd (cons 1 1) 2)
=> (1 . 2)

Re: How to reset the value of associate list element

Ramarren wrote:
Duke wrote:Yet the list form and the quoted form evaluate to the same thing.
Not quite. LIST is a function which creates a list at runtime, whereas QUOTE is a special operator which creates a list at compile time. Quoted forms are treated as source literals, and modifying them has an unspecified behaviour, which in principle might include a memory protection fault if they are placed in read-only memory. I don't think any implementation actually does that, but modifying literals is usually not a good idea.
To be a bit more specific, QUOTE is a special operator, but it merely marks the form that follows it (either an atom or a list) in the source code as being a literal, rather than a form that should be further evaluated (function, macro, or variable reference). But you can, for instance, build up a form that includes QUOTE using LIST, CONS, and other functions and then feed that to EVAL and the right things will occur. But you are correct even in that case that QUOTE marks portions of the source code sexpr as being literal values, which should not be modified because that could actually lead to modification of the source sexpr.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: How to reset the value of associate list element

Just to exemplify what findinglisp said:
CL-USER> (defun literal ()
           (quote (1 2 3)))
LITERAL
CL-USER> (defun constructed ()
           (list 1 2 3))
CONSTRUCTED
CL-USER> (eq (literal) (literal))
T
CL-USER> (eq (constructed) (constructed))
NIL
CL-USER> (setf (cadr (literal)) 50)
50
CL-USER> (literal)
(1 50 3)
CL-USER> (setf (cadr (constructed)) 50)
50
CL-USER> (constructed)
(1 2 3)
You need to be careful not to modify any list created by quote, or, even better, not to create any list with quote unless you are sure it is always gonna be treated as constant data.