Page 1 of 1

How to reset the value of associate list element

Posted: Thu Aug 26, 2010 10:44 am
by joybee
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

Posted: Thu Aug 26, 2010 5:06 pm
by Duke
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.

Code: Select all

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

Code: Select all

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

Re: How to reset the value of associate list element

Posted: Thu Aug 26, 2010 10:04 pm
by edgar-rft

Code: Select all

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

Code: Select all

(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

Posted: Fri Aug 27, 2010 12:18 am
by ramarren
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

Posted: Fri Aug 27, 2010 10:17 am
by joybee
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

Posted: Fri Aug 27, 2010 11:35 am
by Duke
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:

Code: Select all

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

Code: Select all

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

Re: How to reset the value of associate list element

Posted: Fri Aug 27, 2010 11:49 am
by Duke
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:

Code: Select all

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

Code: Select all

(cons 1 (cons 2 nil))
Hopefully you see why EQUAL returns NIL in your example. Try this:

Code: Select all

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

Re: How to reset the value of associate list element

Posted: Fri Aug 27, 2010 11:57 am
by edgar-rft
The particular problem here is that an association list usually is a list of dotted pairs like this:

Code: Select all

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

Code: Select all

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

Code: Select all

(list (cons :a 'a) (cons :b 'b) (cons :c 'c))
=> ((:A . A) (:B . B) (:C . C))
Now also all the code from above works:

Code: Select all

(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

Posted: Sat Aug 28, 2010 7:19 am
by findinglisp
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.

Re: How to reset the value of associate list element

Posted: Sat Aug 28, 2010 8:01 am
by gugamilare
Just to exemplify what findinglisp said:

Code: Select all

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.