(setq orig '((a . 1)
(b . 2)))
(setq added '((b . 0)
(c . 3)
(d . 4)))
;; 操作 1
(mapc (lambda (x) (setf (alist-get (car x) orig) (cdr x))) added)
orig ;; => ((d . 4) (c . 3) (a . 1) (b . 0))
;; 操作 2
(defmacro alist-set! (orig added)
`(mapc (lambda (x) (setf (alist-get (car x) ,orig) (cdr x))) ,added))
(alist-set! orig added)
orig ;; => ((d . 4) (c . 3) (a . 1) (b . 0))
;; 操作 3
(defun alist-set (orig added)
(mapc (lambda (x) (setf (alist-get (car x) orig) (cdr x))) added))
(alist-set orig added)
orig ;; => ((a . 1) (b . 0))
setf
会修改orig
这个list,而外面的orig
跟函数alist-set
里面的orig
并不是同一个variable。
当key,也就是(car x)
,在orig
里的时候,setf
会修改对应的value,由于两个orig
里面的cons cells是共用的,所以如你所见,b
的value的确变成了0。
然而当key不在orig
时,setf
会修改orig
,新增一个element,也就是(setq orig (cons .. orig))
。这就只会修改alist-set
里面的orig
,而不影响外面的orig
。
你是说 函数参数传递的是浅拷贝,他们只共享已存在的cons cell,所以替换是可以的,但是新增的话不会算到原变量的头上。那这么说的话,函数内对传递的参数结构体删除、修改都可以成功,但是新增不行。
那我要是直接对传递的参数pop可以成功,push同样会失败,是吗?难怪我换了好几种写法全失败了。
那如果我想就地新增元素的话,改怎么传递参数呢?
(defun alist-set (orig-var added)
(setq orig (symbol-value orig-var))
(mapc (lambda (x) (setf (alist-get (car x) orig) (cdr x))) added)
(set orig-var orig))
(alist-set 'orig added)
修改了一下,终于可以了,你是直接传递一个symbol,最后直接对symbol重新赋值。我的版本是只传递了一个value,找不到symbol,所以没法新增。
其实新增也行,比方说nconc
。删除的话有delq
,修改的话有rplaca
,(setf (nth ...) ...)
。
重点是那些函数是否destructive,会alter the list。
然而pop
不行,pop
就只是setq
,没有改变list本身。
setf 不算是 destructive 的吗?
setf
也可以只用来赋值。
(lambda (x) (setf x ...))
跟(lambda (x) (setf (car x) ...))
就有区别。
setq
就只是赋值了。
哦哦,多谢。再想请问一个问题, let binding里面的变量该如何理解呢? 比如说:
(setq test '(1 2 3))
;; test 1
(let ((tail (cdr test)))
(setf tail 4))
test ;;=> (1, 2, 3)
;; test 2
(let ((tail (cdr test)))
(setf (cdr tail) 4))
test ;;=> (1 2 . 4)
- let binding中的 cons cell 是共享的吗?和函数传叁的区别在哪里呢?
- 如果1不成功,为何2又突然成功了呢?
一样的,tail
是另外一个variable,所以(setf tail ...)
不影响test
。但(cdr tail)
跟(cdr (cdr test))
是同一个place,所以test也会被修改。
所以 1只是赋值,2中是 in-place 操作。好的,多谢指导!
最好不要对 list 进行 setcdr
setcar
等破坏性操作,也包括 alist 和 plist 这种本质上还是 list 的结构