如何遍历 Emacs lisp 中的文字列表?

How to iterate through a list of literals in Emacs lisp?

我想替换 Emacs 缓冲区中的文本,这个函数有效:

(defun sanitize-md ()
  "Replace characters."
  (interactive)
  (query-replace "\" "/" nil (point-min) (point-max))
  (query-replace "fi" "fi" nil (point-min) (point-max))
  )

我想用一个列表重构它:

(defun sanitize-md ()
  "Replace characters."
  (interactive)
  (let (replacement-list '("\" "/" "fi" "fi"))
    (while (progn
         (let* (
            (to-find (pop replacement-list))
            (to-replace (pop replacement-list))
            )
           (query-replace to-find to-replace nil (point-min) (point-max)))
         (replacement-list)
         ))))

但这会引发错误 Invalid function: "\",即使我引用了列表的声明。我想我对列表开头的 LISP 引号和函数系统感到困惑,所以我尝试 (let (replacement-list '(list "\" "/" "fi" "fi")) 并得到另一个错误 Wrong type argument: stringp, nil.

如何遍历 LISP 中的文字列表直到它为空?

问题出在 let 表单的语法上。第一个参数应该是列表的列表,而不仅仅是一个列表。

另外,您需要使用 (interactive "*") 来避免尝试修改只读的内容。而 let* 形式(你已经使用了正确的语法!)已经是一个单一的形式,所以你可以去掉 progn.

最后,replacement-list不是一个函数,所以你应该在最后一个表达式中去掉它周围的括号。

(defun sanitize-md ()
  "Replace characters."
  (interactive "*")
  (let ((replacement-list '("\" "/" "fi" "fi")))
    (while (let* ((to-find (pop replacement-list))
                  (to-replace (pop replacement-list)) )
             (query-replace to-find to-replace nil (point-min) (point-max))
             replacement-list))) ))

使 replacement-list 成为具有 to-findto-replace 值的 conses 列表可能更惯用,但这至少应该让你回到路上。

注意 replacement-listlet* 的一部分,所以当列表为空时 while 结束。如果query-replacewhile条件中的最后一个形式,没有匹配到则循环结束。参见 this other thread

您可以使用 the dolist macro 轻松遍历模式列表及其替换。在下面的解决方案中,我在列表中将它们配对,以便在每次迭代时都可以访问它们:

(defun sanitize-md ()
  "Replace characters."
  (interactive "*")
  (dolist (from-to '(("\" "/") ("fi" "fi")))
    (query-replace (car from-to) (cdr from-to) nil (point-min) (point-max))))