(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 的结构