简体   繁体   中英

How can I insert into the middle of a list in ELisp?

I have the following data structure as an ELisp variable ( ispell-tex-skip-alists ):

((("---" ispell-tex-arg-end 2)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end 0)
  ("---" ispell-tex-arg-end)
  ("---" . "---"))
 (("---" ispell-tex-arg-end 0)
  ("---" ispell-tex-arg-end 2)
  ("env1" . "endenv1")
  ("env2" . "endenv2")))

I can isolate the part of the list I need with with cddadr , but I need to add an element to this list:

((("---" ispell-tex-arg-end 2)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end)
  ("---" ispell-tex-arg-end 0)
  ("---" ispell-tex-arg-end)
  ("---" . "---"))
 (("---" ispell-tex-arg-end 0)
  ("---" ispell-tex-arg-end 2)
  ("env1" . "endenv1")
  ("env2" . "endenv2")
  ("env3" . "endenv3")))

I have tried add-to-list , but obviously I need a symbol name for the cddadr part of the list (which I could technically optimize to cadr ) for add-to-list to work.

How might I achieve the result I need?


Best attempt:

(defun LaTeX-ispell-skip-environment (env &optional star)
  (let ((start (concat "%s" (if star "\\*?")))
    (end   (concat "\\\\end[    \n]*{[  \n]*%s" (if star "\\*?") "[     \n]*}")))
    (let ((env-list (cddadr ispell-tex-skip-alists)))
      (setcdr (last env-list)
          (cons (format start env)
            (format end env))))))

I assume you are missing a list in your approach:

(defun LaTeX-ispell-skip-environment (env &optional star)
  (let ((start (concat "%s" (if star "\\*?")))
    (end   (concat "\\\\end[    \n]*{[  \n]*%s" (if star "\\*?") "[     \n]*}")))
    (let ((env-list (cddadr ispell-tex-skip-alists)))
      (setcdr (last env-list)
              (list 
               (cons (format start env)
                     (format end env)))))))

In your version the structure of the relevant list changes. Before the modification it is a list of conses. After the modification it is a mixed list. Most of the entries are conses but the last car is a string and the last cdr is also a string.

Alternatively to setcdr and last , you could use nconc :

(defun LaTeX-ispell-skip-environment (env &optional star)
  (let ((start (concat "%s" (if star "\\*?")))
    (end   (concat "\\\\end[    \n]*{[  \n]*%s" (if star "\\*?") "[     \n]*}")))
    (let ((env-list (cddadr ispell-tex-skip-alists)))
      (nconc env-list
             (list 
              (cons (format start env)
                    (format end env)))))))

Test

There follows a test. To make comparison with the wanted result from the original posting possible I replaced "\\\\\\\\end[ \\n]*{[ \\n]*%s" by "end%s" and "[ \\n]*}" by "" in LaTeX-ispell-skip-environment . At the end of the progn the result of (LaTeX-ispell-skip-environment "env3") is tested with equal against the wanted result. The test returns t .

(progn
  (defun LaTeX-ispell-skip-environment (env &optional star)
    (let ((start (concat "%s" (if star "\\*?")))
      (end   (concat "end%s" (if star "\\*?") "")))
      (let ((env-list (cddadr ispell-tex-skip-alists)))
    (setcdr (last env-list)
        (list 
         (cons (format start env)
               (format end env)))))))

  (setq ispell-tex-skip-alists
    '((("---" ispell-tex-arg-end 2)
       ("---" ispell-tex-arg-end)
       ("---" ispell-tex-arg-end)
       ("---" ispell-tex-arg-end)
       ("---" ispell-tex-arg-end 0)
       ("---" ispell-tex-arg-end)
       ("---" . "---"))
      (("---" ispell-tex-arg-end 0)
       ("---" ispell-tex-arg-end 2)
       ("env1" . "endenv1")
       ("env2" . "endenv2"))))

  (LaTeX-ispell-skip-environment "env3")

  (equal ispell-tex-skip-alists
     '((("---" ispell-tex-arg-end 2)
        ("---" ispell-tex-arg-end)
        ("---" ispell-tex-arg-end)
        ("---" ispell-tex-arg-end)
        ("---" ispell-tex-arg-end 0)
        ("---" ispell-tex-arg-end)
        ("---" . "---"))
       (("---" ispell-tex-arg-end 0)
        ("---" ispell-tex-arg-end 2)
        ("env1" . "endenv1")
        ("env2" . "endenv2")
        ("env3" . "endenv3")))))

To answer the question stated in the title more generically, here is a function which inserts an element in the n-th position of a list:

(defun insert-into-list (list el n)
  "Insert into list LIST an element EL at index N.

If N is 0, EL is inserted before the first element.

The resulting list is returned.  As the list contents is mutated
in-place, the old list reference does not remain valid."
  (let* ((padded-list (cons nil list))
         (c (nthcdr n padded-list)))
    (setcdr c (cons el (cdr c)))
    (cdr padded-list)))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM