Lisp:psetf还没有完全理解

147*_*7pm 3 common-lisp

我在SBCL中试图翻转列表中的虚线对:

(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)))
Run Code Online (Sandbox Code Playgroud)

但是我得到了这个(ymmv):

(NIL (O . 24) NIL (R . 47) (K . 49))
Run Code Online (Sandbox Code Playgroud)

这告诉我psetf不喜欢我在做什么.据我了解,psetf具有破坏性和回报NIL.我在这里不理解什么?

sds*_*sds 9

您观察到的行为是正确的和预期的: psetf返回nil 并将mapcar返回值放入返回列表中,因此当num为0时,您到达那里nil,当它为1时,您将获得原始单元格.

轻松修复:

(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))
Run Code Online (Sandbox Code Playgroud)

实际上,CL rotatef 只有你的情况下有一个宏:

(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))
Run Code Online (Sandbox Code Playgroud)

最后,请注意修改引用的数据是一个非常糟糕的主意:

(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
Run Code Online (Sandbox Code Playgroud)

即,细胞是相同的,但列表是不同的.

始终复制所有单元格可能会更好:

(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))
Run Code Online (Sandbox Code Playgroud)