避免修改 quote object 的结构

setcar, nreverse, sort, nconc delq 等属于 Destructive Operations,它们会修改参数的结构。注意避免给它们传入 quote object,比如 '(1 2 3),尤其是在函数中。比如:

(defun foo ()
  (nreverse '(1 2 3)))
;; => foo

(symbol-function 'foo)
;; => (lambda nil (nreverse '(1 2 3)))

(foo)
;; => (3 2 1)

(symbol-function 'foo)
;; => (lambda nil (nreverse '(1)))

(foo)
;; => (1)

Backquote 也可能会有问题

(defun foo ()
  (nreverse `(1 ,2 3)))

(symbol-function 'foo)
;; => (lambda nil (nreverse (cons 1 (cons 2 '(3)))))

正确的做法是确保每个元素都被执行过:

(defun foo ()
  (nreverse (list 1 2 3)))

Emacs Lisp 手册中有不少修改 quote object 的代码举例,但是我们最好把 quote object 当作常量,不要修改它们的结构。而且有一些选项的初始值是 quote object,如:

(defcustom perl-flymake-command '("perl" "-w" "-c")

用户应该避免用 nconc 修改它,而开发者或许用 (list "perl" "-w" "-c") 更好些。


相关问题:

11 个赞

除了 quote object,[1 2 3] 这样的 literal object 也不应该修改。

(defun foo ()
  (nreverse [1 2 3]))
;; => foo

(foo)
;; => [3 2 1]

(foo)
;; => [1 2 3]

此处正确的写法是 (vector 1 2 3)

1 个赞

今天踩坑了 :blush:

(defun funny (&optional arg)
  (interactive "P")
  (let ((order '(foo bar)))
    (and arg
         (setq order (nreverse order)))
    (insert (format ";;=> %s" order))))

(funny)
;;=> (foo bar)
(funny t)
;;=> (bar foo)
(funny t)
;;=> (foo)

An introduction to programming in emacs lisp 里面也提到了[setcar (Programming in Emacs Lisp)],不过楼主的例子更详细,点赞