Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elisp deep copying - consing

Tags:

elisp

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)
    )
like image 875
HexedAgain Avatar asked Jun 02 '13 19:06

HexedAgain


2 Answers

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))
like image 167
djf Avatar answered Oct 23 '22 05:10

djf


Use copy-tree (example assumes you required 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)))
like image 23
danlei Avatar answered Oct 23 '22 06:10

danlei