aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2023-05-19 12:32:28 +0200
committerMattias EngdegÄrd2023-05-19 15:50:42 +0200
commitbd6bba4780dcfdec97ab5e6469f7777c4b2a1b0d (patch)
tree68165f5e80829f975c9e740fd2ca1e06fcc40d2a
parent156973639cc57dec47705f76f63c2ef3dc00a61d (diff)
downloademacs-bd6bba4780dcfdec97ab5e6469f7777c4b2a1b0d.tar.gz
emacs-bd6bba4780dcfdec97ab5e6469f7777c4b2a1b0d.zip
Improved copy-tree documentation and test (bug#63509)
* 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.
-rw-r--r--doc/lispref/lists.texi8
-rw-r--r--doc/lispref/records.texi5
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/emacs-lisp/shortdoc.el2
-rw-r--r--lisp/subr.el21
-rw-r--r--test/lisp/subr-tests.el77
6 files changed, 72 insertions, 47 deletions
diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi
index 16ed0358974..6a00f2887e7 100644
--- a/doc/lispref/lists.texi
+++ b/doc/lispref/lists.texi
@@ -696,7 +696,7 @@ not a list, the sequence's elements do not become elements of the
696resulting list. Instead, the sequence becomes the final @sc{cdr}, like 696resulting list. Instead, the sequence becomes the final @sc{cdr}, like
697any other non-list final argument. 697any other non-list final argument.
698 698
699@defun copy-tree tree &optional vector-like-p 699@defun copy-tree tree &optional vectors-and-records
700This function returns a copy of the tree @var{tree}. If @var{tree} is a 700This function returns a copy of the tree @var{tree}. If @var{tree} is a
701cons cell, this makes a new cons cell with the same @sc{car} and 701cons cell, this makes a new cons cell with the same @sc{car} and
702@sc{cdr}, then recursively copies the @sc{car} and @sc{cdr} in the 702@sc{cdr}, then recursively copies the @sc{car} and @sc{cdr} in the
@@ -704,9 +704,9 @@ same way.
704 704
705Normally, when @var{tree} is anything other than a cons cell, 705Normally, when @var{tree} is anything other than a cons cell,
706@code{copy-tree} simply returns @var{tree}. However, if 706@code{copy-tree} simply returns @var{tree}. However, if
707@var{vector-like-p} is non-@code{nil}, it copies vectors and records 707@var{vectors-and-records} is non-@code{nil}, it copies vectors and records
708too (and operates recursively on their elements). This function 708too (and operates recursively on their elements). The @var{tree}
709cannot cope with circular lists. 709argument must not contain cycles.
710@end defun 710@end defun
711 711
712@defun flatten-tree tree 712@defun flatten-tree tree
diff --git a/doc/lispref/records.texi b/doc/lispref/records.texi
index ebc4569c388..287ad869297 100644
--- a/doc/lispref/records.texi
+++ b/doc/lispref/records.texi
@@ -81,8 +81,9 @@ This function returns a new record with type @var{type} and
81@end example 81@end example
82@end defun 82@end defun
83 83
84To copy records, use @code{copy-tree} with its optional second argument 84To copy trees consisting of records, vectors and conses (lists), use
85non-@code{nil}. @xref{Building Lists, copy-tree}. 85@code{copy-tree} with its optional second argument non-@code{nil}.
86@xref{Building Lists, copy-tree}.
86 87
87@node Backward Compatibility 88@node Backward Compatibility
88@section Backward Compatibility 89@section Backward Compatibility
diff --git a/etc/NEWS b/etc/NEWS
index f1fb70c5fc6..04ef976a8d1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -388,6 +388,9 @@ These hooks were named incorrectly, and so they never actually ran
388when unloading the correspending feature. Instead, you should use 388when unloading the correspending feature. Instead, you should use
389hooks named after the feature name, like 'esh-mode-unload-hook'. 389hooks named after the feature name, like 'esh-mode-unload-hook'.
390 390
391+++
392** 'copy-tree' now copies records when its optional 2nd argument is non-nil.
393
391 394
392* Lisp Changes in Emacs 30.1 395* Lisp Changes in Emacs 30.1
393 396
@@ -585,9 +588,6 @@ Since circular alias chains now cannot occur, 'function-alias-p',
585'indirect-function' and 'indirect-variable' will never signal an error. 588'indirect-function' and 'indirect-variable' will never signal an error.
586Their 'noerror' arguments have no effect and are therefore obsolete. 589Their 'noerror' arguments have no effect and are therefore obsolete.
587 590
588+++
589** 'copy-tree' now copies records when its optional 2nd argument is non-nil.
590
591 591
592* Changes in Emacs 30.1 on Non-Free Operating Systems 592* Changes in Emacs 30.1 on Non-Free Operating Systems
593 593
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 6580e0e4e0c..1e8ab4ad46d 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -834,7 +834,7 @@ A FUNC form can have any number of `:no-eval' (or `:no-value'),
834 :eval (seq-subseq [1 2 3 4 5] 1 3) 834 :eval (seq-subseq [1 2 3 4 5] 1 3)
835 :eval (seq-subseq [1 2 3 4 5] 1)) 835 :eval (seq-subseq [1 2 3 4 5] 1))
836 (copy-tree 836 (copy-tree
837 :eval (copy-tree [1 2 3 4])) 837 :eval (copy-tree [1 (2 3) [4 5]] t))
838 "Mapping Over Vectors" 838 "Mapping Over Vectors"
839 (mapcar 839 (mapcar
840 :eval (mapcar #'identity [1 2 3])) 840 :eval (mapcar #'identity [1 2 3]))
diff --git a/lisp/subr.el b/lisp/subr.el
index 83735933963..5a641965659 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -824,26 +824,31 @@ of course, also replace TO with a slightly larger value
824 next (+ from (* n inc))))) 824 next (+ from (* n inc)))))
825 (nreverse seq)))) 825 (nreverse seq))))
826 826
827(defun copy-tree (tree &optional vector-like-p) 827(defun copy-tree (tree &optional vectors-and-records)
828 "Make a copy of TREE. 828 "Make a copy of TREE.
829If TREE is a cons cell, this recursively copies both its car and its cdr. 829If TREE is a cons cell, this recursively copies both its car and its cdr.
830Contrast to `copy-sequence', which copies only along the cdrs. With second 830Contrast to `copy-sequence', which copies only along the cdrs.
831argument VECTOR-LIKE-P, this copies vectors and records as well as conses." 831With the second argument VECTORS-AND-RECORDS non-nil, this
832traverses and copies vectors and records as well as conses."
832 (declare (side-effect-free error-free)) 833 (declare (side-effect-free error-free))
833 (if (consp tree) 834 (if (consp tree)
834 (let (result) 835 (let (result)
835 (while (consp tree) 836 (while (consp tree)
836 (let ((newcar (car tree))) 837 (let ((newcar (car tree)))
837 (if (or (consp (car tree)) (and vector-like-p (or (vectorp (car tree)) (recordp (car tree))))) 838 (if (or (consp (car tree))
838 (setq newcar (copy-tree (car tree) vector-like-p))) 839 (and vectors-and-records
840 (or (vectorp (car tree)) (recordp (car tree)))))
841 (setq newcar (copy-tree (car tree) vectors-and-records)))
839 (push newcar result)) 842 (push newcar result))
840 (setq tree (cdr tree))) 843 (setq tree (cdr tree)))
841 (nconc (nreverse result) 844 (nconc (nreverse result)
842 (if (and vector-like-p (or (vectorp tree) (recordp tree))) (copy-tree tree vector-like-p) tree))) 845 (if (and vectors-and-records (or (vectorp tree) (recordp tree)))
843 (if (and vector-like-p (or (vectorp tree) (recordp tree))) 846 (copy-tree tree vectors-and-records)
847 tree)))
848 (if (and vectors-and-records (or (vectorp tree) (recordp tree)))
844 (let ((i (length (setq tree (copy-sequence tree))))) 849 (let ((i (length (setq tree (copy-sequence tree)))))
845 (while (>= (setq i (1- i)) 0) 850 (while (>= (setq i (1- i)) 0)
846 (aset tree i (copy-tree (aref tree i) vector-like-p))) 851 (aset tree i (copy-tree (aref tree i) vectors-and-records)))
847 tree) 852 tree)
848 tree))) 853 tree)))
849 854
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 4ebb68556be..1c220b1da18 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -1207,35 +1207,54 @@ final or penultimate step during initialization."))
1207 (should (eq a a-dedup)))) 1207 (should (eq a a-dedup))))
1208 1208
1209(ert-deftest subr--copy-tree () 1209(ert-deftest subr--copy-tree ()
1210 (should (eq (copy-tree nil) nil)) 1210 ;; Check that values other than conses, vectors and records are
1211 (let* ((a (list (list "a") "b" (list "c") "g")) 1211 ;; neither copied nor traversed.
1212 (copy1 (copy-tree a)) 1212 (let ((s (propertize "abc" 'prop (list 11 12)))
1213 (copy2 (copy-tree a t))) 1213 (h (make-hash-table :test #'equal)))
1214 (should (equal a copy1)) 1214 (puthash (list 1 2) (list 3 4) h)
1215 (should (equal a copy2)) 1215 (dolist (x (list nil 'a "abc" s h))
1216 (should-not (eq a copy1)) 1216 (should (eq (copy-tree x) x))
1217 (should-not (eq a copy2))) 1217 (should (eq (copy-tree x t) x))))
1218 (let* ((a (list (list "a") "b" (list "c" (record 'foo "d")) (list ["e" "f"]) "g")) 1218
1219 (copy1 (copy-tree a)) 1219 ;; Use the printer to detect common parts of Lisp values.
1220 (copy2 (copy-tree a t))) 1220 (let ((print-circle t))
1221 (should (equal a copy1)) 1221 (cl-labels ((prn3 (x y z) (prin1-to-string (list x y z)))
1222 (should (equal a copy2)) 1222 (cat3 (x y z) (concat "(" x " " y " " z ")")))
1223 (should-not (eq a copy1)) 1223 (let ((x '(a (b ((c) . d) e) (f))))
1224 (should-not (eq a copy2))) 1224 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1225 (let* ((a (record 'foo "a" (record 'bar "b"))) 1225 (cat3 "(a (b ((c) . d) e) (f))"
1226 (copy1 (copy-tree a)) 1226 "(a (b ((c) . d) e) (f))"
1227 (copy2 (copy-tree a t))) 1227 "(a (b ((c) . d) e) (f))"))))
1228 (should (equal a copy1)) 1228 (let ((x '(a [b (c d)] #s(e (f [g])))))
1229 (should (equal a copy2)) 1229 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1230 (should (eq a copy1)) 1230 (cat3 "(a #1=[b (c d)] #2=#s(e (f [g])))"
1231 (should-not (eq a copy2))) 1231 "(a #1# #2#)"
1232 (let* ((a ["a" "b" ["c" ["d"]]]) 1232 "(a [b (c d)] #s(e (f [g])))"))))
1233 (copy1 (copy-tree a)) 1233 (let ((x [a (b #s(c d))]))
1234 (copy2 (copy-tree a t))) 1234 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1235 (should (equal a copy1)) 1235 (cat3 "#1=[a (b #s(c d))]"
1236 (should (equal a copy2)) 1236 "#1#"
1237 (should (eq a copy1)) 1237 "[a (b #s(c d))]"))))
1238 (should-not (eq a copy2)))) 1238 (let ((x #s(a (b [c d]))))
1239 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1240 (cat3 "#1=#s(a (b [c d]))"
1241 "#1#"
1242 "#s(a (b [c d]))"))))
1243 ;; Check cdr recursion.
1244 (let ((x '(a b . [(c . #s(d))])))
1245 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1246 (cat3 "(a b . #1=[(c . #s(d))])"
1247 "(a b . #1#)"
1248 "(a b . [(c . #s(d))])"))))
1249 ;; Check that we can copy DAGs (the result is a tree).
1250 (let ((x (list '(a b) nil [c d] nil #s(e f) nil)))
1251 (setf (nth 1 x) (nth 0 x))
1252 (setf (nth 3 x) (nth 2 x))
1253 (setf (nth 5 x) (nth 4 x))
1254 (should (equal (prn3 x (copy-tree x) (copy-tree x t))
1255 (cat3 "(#1=(a b) #1# #2=[c d] #2# #3=#s(e f) #3#)"
1256 "((a b) (a b) #2# #2# #3# #3#)"
1257 "((a b) (a b) [c d] [c d] #s(e f) #s(e f))")))))))
1239 1258
1240(provide 'subr-tests) 1259(provide 'subr-tests)
1241;;; subr-tests.el ends here 1260;;; subr-tests.el ends here