I'm trying to implement my own deep-copy routine in elisp (since something like (setq newlist oldlist)
seems to give just a shallow copy, and (copy-sequence newlist oldlist)
still leaves newlist
exposed to any changes of the elements of oldlist
)
Moreover, if there is a function that does what I want I am having no luck finding it.
The definition of my function is:
(defun deep-copy (iList oList)
(setq oList (car iList))
(setq counter (- (length iList) 1))
(setq iList (cdr iList))
(while (> counter 0)
(setq oList (cons oList (car iList)))
(setq iList (cdr iList))
(setq counter (- counter 1) )))
and afterwards, with an iList
of (1 2 3 4 5 6)
what oList
happens to have is: (((((1 . 2) . 3) . 4) . 5) . 6)
i.e. nested lists.
I have tried quoting, back quoting, using append, switching the order of oList
and (car iList)
in (cons # #)
, googling for a solution, but I am having no luck (either errors or garbage).
In addition to any welcome comments on what functions already exist that will do what I want, where there are weaknesses in the code (I am an elisp newbie), could someone tell me how to cons elements to an existing list properly?
the examples tend be variants of the form: (cons 'pine '(fir oak maple))
, where '(fir oak maple)
is some hard coded list
edit: For the last two hours I have been battling against myself (since I commented out oList in the calling function, and I kept referring to an old version of it). At any rate, swapping oList
and (car iList)
and then reversing at the end seems to do the trick (but surely there is a better way!?) i.e.
(defun deep-copy (iList)
(setq oList nil )
(setq counter (- (length iList) 1))
(while (>= counter 0)
(setq oList (cons (car iList) oList) )
(setq iList (cdr iList) )
(setq counter (- counter 1) ))
(reverse oList)
)
Elisp has the function copy-tree
. It's the recursive version of copy-sequence
:
Example
(let* ((orig '((1 2) (3 4)))
(copy (copy-tree orig)))
(setcdr (cadr copy) '(0))
(list orig copy))
==> (((1 2) (3 4)) ((1 2) (3 0)))
in your case you could write:
(setq oList (copy-tree iList))
Use copy-tree
(example assumes you require
d cl
, for my convenience, but copy-tree
itself doesn't require it):
elisp> (setq list1 '(((1 2) (3 4)) 5 (6)))
(((1 2)
(3 4))
5
(6))
elisp> (setq list2 (copy-sequence list1))
(((1 2)
(3 4))
5
(6))
elisp> (setf (caar list2) 1)
1
elisp> list2
((1
(3 4))
5
(6))
elisp> list1
((1
(3 4))
5
(6))
elisp> (setq list1 '(((1 2) (3 4)) 5 (6)))
(((1 2)
(3 4))
5
(6))
elisp> (setq list2 (copy-tree list1))
(((1 2)
(3 4))
5
(6))
elisp> (setf (caar list2) 1)
1
elisp> list1
(((1 2)
(3 4))
5
(6))
elisp> list2
((1
(3 4))
5
(6))
Instead of giving tips about your code, I suggest that you read through the Elisp introduction that comes with Emacs: C-h i g (eintr) RET
or other introductory Lisp books, for example Touretzky (the latter is for Common Lisp, but a great introduction). It will teach you the basics – for example, to not just setq
in function definitions and so on.
But to give you an example, here's the definition of copy-tree
(alternatively, just view it in your Emacs: M-x find-function RET copy-tree RET
):
(defun copy-tree (tree &optional vecp)
"Make a copy of TREE.
If TREE is a cons cell, this recursively copies both its car and its cdr.
Contrast to `copy-sequence', which copies only along the cdrs. With second
argument VECP, this copies vectors as well as conses."
(if (consp tree)
(let (result)
(while (consp tree)
(let ((newcar (car tree)))
(if (or (consp (car tree)) (and vecp (vectorp (car tree))))
(setq newcar (copy-tree (car tree) vecp)))
(push newcar result))
(setq tree (cdr tree)))
(nconc (nreverse result) tree))
(if (and vecp (vectorp tree))
(let ((i (length (setq tree (copy-sequence tree)))))
(while (>= (setq i (1- i)) 0)
(aset tree i (copy-tree (aref tree i) vecp)))
tree)
tree)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With