* etc/NEWS: Move entry since it's an incompatible change.
* lisp/emacs-lisp/shortdoc.el (vector): Make the example relevant.
* lisp/subr.el (copy-tree): Rename second argument,
since 'vector-like' is a term with a specific meaning in Emacs
but not the one intended here.
* doc/lispref/lists.texi (Building Lists): Rename second argument,
and make it clear that the input must be acyclic.
* doc/lispref/records.texi (Record Functions):
Be more precise: `copy-sequence` is used to copy records,
`copy-tree` copies trees made of records etc.
* test/lisp/subr-tests.el (subr--copy-tree): Extend and strengthen the
test considerably, using the print-circle trick to detect structure
sharing precisely.
resulting list. Instead, the sequence becomes the final @sc{cdr}, like
any other non-list final argument.
-@defun copy-tree tree &optional vector-like-p
+@defun copy-tree tree &optional vectors-and-records
This function returns a copy of the tree @var{tree}. If @var{tree} is a
cons cell, this makes a new cons cell with the same @sc{car} and
@sc{cdr}, then recursively copies the @sc{car} and @sc{cdr} in the
Normally, when @var{tree} is anything other than a cons cell,
@code{copy-tree} simply returns @var{tree}. However, if
-@var{vector-like-p} is non-@code{nil}, it copies vectors and records
-too (and operates recursively on their elements). This function
-cannot cope with circular lists.
+@var{vectors-and-records} is non-@code{nil}, it copies vectors and records
+too (and operates recursively on their elements). The @var{tree}
+argument must not contain cycles.
@end defun
@defun flatten-tree tree
@end example
@end defun
-To copy records, use @code{copy-tree} with its optional second argument
-non-@code{nil}. @xref{Building Lists, copy-tree}.
+To copy trees consisting of records, vectors and conses (lists), use
+@code{copy-tree} with its optional second argument non-@code{nil}.
+@xref{Building Lists, copy-tree}.
@node Backward Compatibility
@section Backward Compatibility
when unloading the correspending feature. Instead, you should use
hooks named after the feature name, like 'esh-mode-unload-hook'.
++++
+** 'copy-tree' now copies records when its optional 2nd argument is non-nil.
+
\f
* Lisp Changes in Emacs 30.1
'indirect-function' and 'indirect-variable' will never signal an error.
Their 'noerror' arguments have no effect and are therefore obsolete.
-+++
-** 'copy-tree' now copies records when its optional 2nd argument is non-nil.
-
\f
* Changes in Emacs 30.1 on Non-Free Operating Systems
:eval (seq-subseq [1 2 3 4 5] 1 3)
:eval (seq-subseq [1 2 3 4 5] 1))
(copy-tree
- :eval (copy-tree [1 2 3 4]))
+ :eval (copy-tree [1 (2 3) [4 5]] t))
"Mapping Over Vectors"
(mapcar
:eval (mapcar #'identity [1 2 3]))
next (+ from (* n inc)))))
(nreverse seq))))
-(defun copy-tree (tree &optional vector-like-p)
+(defun copy-tree (tree &optional vectors-and-records)
"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 VECTOR-LIKE-P, this copies vectors and records as well as conses."
+Contrast to `copy-sequence', which copies only along the cdrs.
+With the second argument VECTORS-AND-RECORDS non-nil, this
+traverses and copies vectors and records as well as conses."
(declare (side-effect-free error-free))
(if (consp tree)
(let (result)
(while (consp tree)
(let ((newcar (car tree)))
- (if (or (consp (car tree)) (and vector-like-p (or (vectorp (car tree)) (recordp (car tree)))))
- (setq newcar (copy-tree (car tree) vector-like-p)))
+ (if (or (consp (car tree))
+ (and vectors-and-records
+ (or (vectorp (car tree)) (recordp (car tree)))))
+ (setq newcar (copy-tree (car tree) vectors-and-records)))
(push newcar result))
(setq tree (cdr tree)))
(nconc (nreverse result)
- (if (and vector-like-p (or (vectorp tree) (recordp tree))) (copy-tree tree vector-like-p) tree)))
- (if (and vector-like-p (or (vectorp tree) (recordp tree)))
+ (if (and vectors-and-records (or (vectorp tree) (recordp tree)))
+ (copy-tree tree vectors-and-records)
+ tree)))
+ (if (and vectors-and-records (or (vectorp tree) (recordp tree)))
(let ((i (length (setq tree (copy-sequence tree)))))
(while (>= (setq i (1- i)) 0)
- (aset tree i (copy-tree (aref tree i) vector-like-p)))
+ (aset tree i (copy-tree (aref tree i) vectors-and-records)))
tree)
tree)))
(should (eq a a-dedup))))
(ert-deftest subr--copy-tree ()
- (should (eq (copy-tree nil) nil))
- (let* ((a (list (list "a") "b" (list "c") "g"))
- (copy1 (copy-tree a))
- (copy2 (copy-tree a t)))
- (should (equal a copy1))
- (should (equal a copy2))
- (should-not (eq a copy1))
- (should-not (eq a copy2)))
- (let* ((a (list (list "a") "b" (list "c" (record 'foo "d")) (list ["e" "f"]) "g"))
- (copy1 (copy-tree a))
- (copy2 (copy-tree a t)))
- (should (equal a copy1))
- (should (equal a copy2))
- (should-not (eq a copy1))
- (should-not (eq a copy2)))
- (let* ((a (record 'foo "a" (record 'bar "b")))
- (copy1 (copy-tree a))
- (copy2 (copy-tree a t)))
- (should (equal a copy1))
- (should (equal a copy2))
- (should (eq a copy1))
- (should-not (eq a copy2)))
- (let* ((a ["a" "b" ["c" ["d"]]])
- (copy1 (copy-tree a))
- (copy2 (copy-tree a t)))
- (should (equal a copy1))
- (should (equal a copy2))
- (should (eq a copy1))
- (should-not (eq a copy2))))
+ ;; Check that values other than conses, vectors and records are
+ ;; neither copied nor traversed.
+ (let ((s (propertize "abc" 'prop (list 11 12)))
+ (h (make-hash-table :test #'equal)))
+ (puthash (list 1 2) (list 3 4) h)
+ (dolist (x (list nil 'a "abc" s h))
+ (should (eq (copy-tree x) x))
+ (should (eq (copy-tree x t) x))))
+
+ ;; Use the printer to detect common parts of Lisp values.
+ (let ((print-circle t))
+ (cl-labels ((prn3 (x y z) (prin1-to-string (list x y z)))
+ (cat3 (x y z) (concat "(" x " " y " " z ")")))
+ (let ((x '(a (b ((c) . d) e) (f))))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "(a (b ((c) . d) e) (f))"
+ "(a (b ((c) . d) e) (f))"
+ "(a (b ((c) . d) e) (f))"))))
+ (let ((x '(a [b (c d)] #s(e (f [g])))))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "(a #1=[b (c d)] #2=#s(e (f [g])))"
+ "(a #1# #2#)"
+ "(a [b (c d)] #s(e (f [g])))"))))
+ (let ((x [a (b #s(c d))]))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "#1=[a (b #s(c d))]"
+ "#1#"
+ "[a (b #s(c d))]"))))
+ (let ((x #s(a (b [c d]))))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "#1=#s(a (b [c d]))"
+ "#1#"
+ "#s(a (b [c d]))"))))
+ ;; Check cdr recursion.
+ (let ((x '(a b . [(c . #s(d))])))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "(a b . #1=[(c . #s(d))])"
+ "(a b . #1#)"
+ "(a b . [(c . #s(d))])"))))
+ ;; Check that we can copy DAGs (the result is a tree).
+ (let ((x (list '(a b) nil [c d] nil #s(e f) nil)))
+ (setf (nth 1 x) (nth 0 x))
+ (setf (nth 3 x) (nth 2 x))
+ (setf (nth 5 x) (nth 4 x))
+ (should (equal (prn3 x (copy-tree x) (copy-tree x t))
+ (cat3 "(#1=(a b) #1# #2=[c d] #2# #3=#s(e f) #3#)"
+ "((a b) (a b) #2# #2# #3# #3#)"
+ "((a b) (a b) [c d] [c d] #s(e f) #s(e f))")))))))
(provide 'subr-tests)
;;; subr-tests.el ends here