3

I'm trying this in SBCL to flip the dotted pairs in the list:

(mapcar (lambda (x) (let ((num (random 2)))
                          (if (= num 0)
                             (psetf (cdr x) (car x) (car x) (cdr x))
                            x)))
                 '((B . 21) (O . 24) (P . 15) (R . 47) (K . 49)))

However I get this (ymmv):

(NIL (O . 24) NIL (R . 47) (K . 49))

which tells me psetf is not liking what I'm doing. As I understand, psetf is destructive and returns NIL. What am I not understanding here?

147pm
  • 2,137
  • 18
  • 28

2 Answers2

9

The behavior you observe is correct and expected: psetf returns nil and mapcar places the return value into the return list, so when num is 0, you get there nil, and when it is 1, you get the original cell.

Easily fixed:

(mapcar (lambda (x)
          (when (zerop (random 2))
            (psetf (cdr x) (car x) (car x) (cdr x)))
          x)
        '((B . 21) (O . 24) (P . 15) (R . 47) (K . 49)))
==> ((B . 21) (24 . O) (P . 15) (47 . R) (K . 49))

actually, CL has a macro rotatef just for your case:

(mapcar (lambda (x)
          (when (zerop (random 2))
            (rotatef (cdr x) (car x)))
          x)
        '((B . 21) (O . 24) (P . 15) (R . 47) (K . 49)))
==> ((21 . B) (O . 24) (15 . P) (R . 47) (K . 49))

Finally, please note that modifying quoted data is a very bad idea:

(defparameter *alist-0* '((B . 21) (O . 24) (P . 15) (R . 47) (K . 49)))
(defparameter *alist-1*
  (mapcar (lambda (x)
            (when (zerop (random 2))
              (rotatef (cdr x) (car x)))
            x)
          *alist-0*))
(eq *alist-0* *alist-1*)
==> nil
(equal *alist-0* *alist-1*)
==> t ; !!!
(every #'eq *alist-0* *alist-1*)
==> t

i.e., the cells are the same, but the lists are different.

It would probably be better to consistently copy all cells:

(defparameter *alist-2*
  (mapcar (lambda (x)
            (if (zerop (random 2))
                (cons (cdr x) (car x))
                (cons (car x) (cdr x))))
          *alist-0*))
*alist-0*
==> ((B . 21) (O . 24) (P . 15) (R . 47) (K . 49))
*alist-2*
==> ((21 . B) (O . 24) (15 . P) (R . 47) (K . 49))
sds
  • 58,617
  • 29
  • 161
  • 278
3

Often one might want to avoid modifying the cons cells. cons new ones.

CL-USER 76 > (mapcar (lambda (pair)
                       (if (= (random 2) 0)
                           (cons (cdr pair)
                                 (car pair))
                         pair))
                     '((B . 21) (O . 24) (P . 15) (R . 47) (K . 49)))
((21 . B) (24 . O) (P . 15) (47 . R) (49 . K))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346