emonigma
emonigma

Reputation: 4436

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

I want to replace text in an Emacs buffer and this function works:

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

I want to refactor it with a list with:

(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)
         ))))

But this throws an error Invalid function: "\\", even though I quoted the declaration of the list. I think I am getting confused with LISP's system of quotes and functions at the start of a list, so I tried (let (replacement-list '(list "\\" "/" "fi" "fi")) and got another error Wrong type argument: stringp, nil.

How to iterate through a list of literals in LISP until it is empty?

Upvotes: 0

Views: 1481

Answers (2)

tripleee
tripleee

Reputation: 189809

The problem is with the let form's syntax. The first argument should be a list of lists, not just a single list.

Also, you will want to use (interactive "*") to avoid attempting to modify content which is read-only. And the let* form (where you are already using the correct syntax!) is already a single form, so you can take out the progn.

Finally, replacement-list is not a function, so you should take out the parentheses around it in the last expression.

(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))) ))

It would perhaps be more idiomatic to make replacement-list be a list of conses with to-find and to-replace values, but this should at least get you back up onto the road.

Note that when replacement-list is the last form of of the let* structure, that is then what the while expression evaluates, thus ending the loop when the list is empty. If query-replace were the last form in the condition of while, the loop would end when no match is found. See this other thread.

Upvotes: 4

Steve Vinoski
Steve Vinoski

Reputation: 20024

You can use the dolist macro to easily walk a list of patterns and their replacements. In the solution below I've made them pairs in the list so that both are accessible at each iteration:

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

Upvotes: 1

Related Questions