unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Predicate for true lists
@ 2018-04-16 19:34 Basil L. Contovounesios
  2018-06-04 12:12 ` Basil L. Contovounesios
  0 siblings, 1 reply; 100+ messages in thread
From: Basil L. Contovounesios @ 2018-04-16 19:34 UTC (permalink / raw)
  To: emacs-devel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Add predicate 'list-true-p' --]
[-- Type: text/x-diff, Size: 11132 bytes --]

From 15a5e9f48543dc114c2a9fe69fabddd0d41ec24c Mon Sep 17 00:00:00 2001
From: "Basil L. Contovounesios" <contovob@tcd.ie>
Date: Mon, 16 Apr 2018 17:47:22 +0100
Subject: [PATCH] Add predicate 'list-true-p'

* lisp/subr.el (list-true-p): New function.
* doc/lispref/lists.texi (List-related Predicates):
* etc/NEWS: Mention it.
* lisp/emacs-lisp/byte-opt.el (byte-optimize-if):
* lisp/emacs-lisp/cl-macs.el (cl--make-usage-args):
* lisp/org/ob-core.el (org-babel-insert-result): Use it.
* lisp/format.el (format-proper-list-p): Remove.
(format-annotate-single-property-change): Use list-true-p instead.
* lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove.
(ert--explain-equal-rec): Use list-true-p instead.
* test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p):
Move from here...
* test/lisp/subr-tests.el (subr-tests--list-true-p): ...to here,
mutatis mutandis.
---
 doc/lispref/lists.texi            | 16 ++++++++++++
 etc/NEWS                          |  4 +++
 lisp/emacs-lisp/byte-opt.el       |  3 +--
 lisp/emacs-lisp/cl-macs.el        |  2 +-
 lisp/emacs-lisp/ert.el            | 22 ++++------------
 lisp/format.el                    | 12 ++-------
 lisp/org/ob-core.el               |  5 ++--
 lisp/subr.el                      |  6 +++++
 test/lisp/emacs-lisp/ert-tests.el | 42 -------------------------------
 test/lisp/subr-tests.el           | 18 +++++++++++++
 10 files changed, 55 insertions(+), 75 deletions(-)

diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi
index 761750eb20..62c14e963a 100644
--- a/doc/lispref/lists.texi
+++ b/doc/lispref/lists.texi
@@ -153,6 +153,22 @@ List-related Predicates
 @end example
 @end defun
 
+@defun list-true-p object
+This function returns @code{t} if OBJECT is a true list, @code{nil}
+otherwise.  In addition to satistying @code{listp}, a true list is
+neither circular nor dotted.
+
+@example
+@group
+(list-true-p '(1 2 3))
+     @result{} t
+@end group
+@group
+(list-true-p '(1 2 . 3))
+     @result{} nil
+@end group
+@end example
+@end defun
 
 @node List Elements
 @section Accessing Elements of Lists
diff --git a/etc/NEWS b/etc/NEWS
index 5aa92e2991..c20b9ade97 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -487,6 +487,10 @@ x-lost-selection-hooks, x-sent-selection-hooks
 \f
 * Lisp Changes in Emacs 27.1
 
++++
+** New function 'list-true-p' returns t for true lists which are
+neither circular nor dotted.
+
 +++
 ** New function assoc-delete-all.
 
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 3bc4c438d6..d62ee2b95c 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -982,8 +982,7 @@ byte-optimize-if
   ;; (if <test> <then> nil) ==> (if <test> <then>)
   (let ((clause (nth 1 form)))
     (cond ((and (eq (car-safe clause) 'progn)
-                ;; `clause' is a proper list.
-                (null (cdr (last clause))))
+                (list-true-p clause))
            (if (null (cddr clause))
                ;; A trivial `progn'.
                (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form)))
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 9600230c07..2ed8347ba8 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -498,7 +498,7 @@ cl--make-usage-args
       ;; `&aux' args aren't arguments, so let's just drop them from the
       ;; usage info.
       (setq arglist (cl-subseq arglist 0 aux))))
-  (if (cdr-safe (last arglist))         ;Not a proper list.
+  (if (not (list-true-p arglist))
       (let* ((last (last arglist))
              (tail (cdr last)))
         (unwind-protect
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 32bb367cdb..4134511f5d 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -472,18 +472,6 @@ ert--should-error-handle-error
 ;; buffer.  Perhaps explanations should be reported through `ert-info'
 ;; rather than as part of the condition.
 
-(defun ert--proper-list-p (x)
-  "Return non-nil if X is a proper list, nil otherwise."
-  (cl-loop
-   for firstp = t then nil
-   for fast = x then (cddr fast)
-   for slow = x then (cdr slow) do
-   (when (null fast) (cl-return t))
-   (when (not (consp fast)) (cl-return nil))
-   (when (null (cdr fast)) (cl-return t))
-   (when (not (consp (cdr fast))) (cl-return nil))
-   (when (and (not firstp) (eq fast slow)) (cl-return nil))))
-
 (defun ert--explain-format-atom (x)
   "Format the atom X for `ert--explain-equal'."
   (pcase x
@@ -498,12 +486,12 @@ ert--explain-equal-rec
       `(different-types ,a ,b)
     (pcase-exhaustive a
       ((pred consp)
-       (let ((a-proper-p (ert--proper-list-p a))
-             (b-proper-p (ert--proper-list-p b)))
-         (if (not (eql (not a-proper-p) (not b-proper-p)))
+       (let ((a-proper-p (list-true-p a))
+             (b-proper-p (list-true-p b)))
+         (if (not (eq (not a-proper-p) (not b-proper-p)))
              `(one-list-proper-one-improper ,a ,b)
            (if a-proper-p
-               (if (not (equal (length a) (length b)))
+               (if (/= (length a) (length b))
                    `(proper-lists-of-different-length ,(length a) ,(length b)
                                                       ,a ,b
                                                       first-mismatch-at
@@ -523,7 +511,7 @@ ert--explain-equal-rec
                      (cl-assert (equal a b) t)
                      nil))))))))
       ((pred arrayp)
-       (if (not (equal (length a) (length b)))
+       (if (/= (length a) (length b))
            `(arrays-of-different-length ,(length a) ,(length b)
                                         ,a ,b
                                         ,@(unless (char-table-p a)
diff --git a/lisp/format.el b/lisp/format.el
index 2f198e3eb7..e5bc60712b 100644
--- a/lisp/format.el
+++ b/lisp/format.el
@@ -539,14 +539,6 @@ format-make-relatively-unique
 	(setq tail next)))
     (cons acopy bcopy)))
 
-(defun format-proper-list-p (list)
-  "Return t if LIST is a proper list.
-A proper list is a list ending with a nil cdr, not with an atom "
-  (when (listp list)
-    (while (consp list)
-      (setq list (cdr list)))
-    (null list)))
-
 (defun format-reorder (items order)
   "Arrange ITEMS to follow partial ORDER.
 Elements of ITEMS equal to elements of ORDER will be rearranged
@@ -1005,8 +997,8 @@ format-annotate-single-property-change
       ;; If either old or new is a list, have to treat both that way.
       (if (and (or (listp old) (listp new))
 	       (not (get prop 'format-list-atomic-p)))
-	  (if (or (not (format-proper-list-p old))
-		  (not (format-proper-list-p new)))
+          (if (not (and (list-true-p old)
+                        (list-true-p new)))
 	      (format-annotate-atomic-property-change prop-alist old new)
 	    (let* ((old (if (listp old) old (list old)))
 		   (new (if (listp new) new (list new)))
diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el
index 5d5faaa6fd..f931bb3c31 100644
--- a/lisp/org/ob-core.el
+++ b/lisp/org/ob-core.el
@@ -2310,10 +2310,9 @@ org-babel-insert-result
 		       (lambda (r)
 			 ;; Non-nil when result R can be turned into
 			 ;; a table.
-			 (and (listp r)
-			      (null (cdr (last r)))
+                         (and (list-true-p r)
 			      (cl-every
-			       (lambda (e) (or (atom e) (null (cdr (last e)))))
+                               (lambda (e) (or (atom e) (list-true-p e)))
 			       result)))))
 		  ;; insert results based on type
 		  (cond
diff --git a/lisp/subr.el b/lisp/subr.el
index 9cf7d596cd..4a0ab321bc 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -548,6 +548,12 @@ nbutlast
 	   (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil))
 	   list))))
 
+(defun list-true-p (object)
+  "Return t if OBJECT is a true list.
+A true list is neither circular nor dotted (i.e., its last `cdr'
+is nil)."
+  (null (nthcdr (safe-length object) object)))
+
 (defun zerop (number)
   "Return t if NUMBER is zero."
   ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because
diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el
index e92b434274..cb957bd9fd 100644
--- a/test/lisp/emacs-lisp/ert-tests.el
+++ b/test/lisp/emacs-lisp/ert-tests.el
@@ -496,48 +496,6 @@ ert-test--which-file
 
 
 ;;; Tests for utility functions.
-(ert-deftest ert-test-proper-list-p ()
-  (should (ert--proper-list-p '()))
-  (should (ert--proper-list-p '(1)))
-  (should (ert--proper-list-p '(1 2)))
-  (should (ert--proper-list-p '(1 2 3)))
-  (should (ert--proper-list-p '(1 2 3 4)))
-  (should (not (ert--proper-list-p 'a)))
-  (should (not (ert--proper-list-p '(1 . a))))
-  (should (not (ert--proper-list-p '(1 2 . a))))
-  (should (not (ert--proper-list-p '(1 2 3 . a))))
-  (should (not (ert--proper-list-p '(1 2 3 4 . a))))
-  (let ((a (list 1)))
-    (setf (cdr (last a)) a)
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2)))
-    (setf (cdr (last a)) a)
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3)))
-    (setf (cdr (last a)) a)
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3 4)))
-    (setf (cdr (last a)) a)
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2)))
-    (setf (cdr (last a)) (cdr a))
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3)))
-    (setf (cdr (last a)) (cdr a))
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3 4)))
-    (setf (cdr (last a)) (cdr a))
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3)))
-    (setf (cdr (last a)) (cddr a))
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3 4)))
-    (setf (cdr (last a)) (cddr a))
-    (should (not (ert--proper-list-p a))))
-  (let ((a (list 1 2 3 4)))
-    (setf (cdr (last a)) (cl-cdddr a))
-    (should (not (ert--proper-list-p a)))))
-
 (ert-deftest ert-test-parse-keys-and-body ()
   (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo))))
   (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil)))
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 52b61d9fb9..63fe7ee139 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -306,6 +306,24 @@ subr-test--frames-1
   (should (eq (string-to-char (symbol-name (gensym))) ?g))
   (should (eq (string-to-char (symbol-name (gensym "X"))) ?X)))
 
+(ert-deftest subr-tests--list-true-p ()
+  "Test `list-true-p' behavior."
+  (dotimes (length 4)
+    ;; True and dotted lists
+    (let ((list (make-list length 0)))
+      (should (list-true-p list))
+      (should (not (list-true-p (nconc list 0)))))
+    ;; Circular lists
+    (dotimes (n (1+ length))
+      (let ((circle (make-list (1+ length) 0)))
+        (should (not (list-true-p (nconc circle (nthcdr n circle))))))))
+  ;; Atoms
+  (should (not (list-true-p 0)))
+  (should (not (list-true-p "")))
+  (should (not (list-true-p [])))
+  (should (not (list-true-p (make-bool-vector 0 nil))))
+  (should (not (list-true-p (make-symbol "a")))))
+
 (ert-deftest subr-tests--assq-delete-all ()
   "Test `assq-delete-all' behavior."
   (cl-flet ((new-list-fn
-- 
2.17.0


[-- Attachment #2: Benchmark for 'list-true-p' --]
[-- Type: application/emacs-lisp, Size: 873 bytes --]

[-- Attachment #3: Type: text/plain, Size: 1757 bytes --]


A while ago, I added a little convenience function to my init file[1]
to determine whether a given object is a true list (as opposed to a
circular or dotted one):

        (null (nthcdr (safe-length object) object))

[1]: https://github.com/basil-conto/dotfiles/blob/96aeb904a6fd94d9fbcf95483fd4f79194e90592/.emacs.d/lisp/blc-lib.el#L46-L49

Since then, I have noticed variations of this predicate/condition strewn
around the Emacs sources, albeit usually implemented less efficiently.

Would such a predicate be a welcome addition to, say, subr.el or
subr-x.el?  I attach a first draft of a patch targeting subr.el for your
consideration.

Also attached is a toy benchmark comparing the new function list-true-p
with the existing format-proper-list-p and ert--proper-list-p when
given, in turn, a true, dotted, and circular list as argument.
It prints the following, after a couple of runs:

        ‘format-proper-list-p’
          true     (0.16966186900000002  0 0.0)
          dotted   (0.168859839          0 0.0)
          circular (0.244791363          0 0.0)
        ‘ert--proper-list-p’
          true     (0.622797443          0 0.0)
          dotted   (0.621622385          0 0.0)
          circular (0.9150398590000001   0 0.0)
        ‘list-true-p’
          true     (0.042970005000000006 0 0.0)
          dotted   (0.04294060500000001  0 0.0)
          circular (0.057346661          0 0.0)

P.S. What is the preferred way of formatting car/cdr in docstrings?
     The manuals seem to use small caps CAR/CDR and subr.el seems to
     alternate between no quotes and `car'/`cdr'.  I have gone with the
     latter for the docstring of list-true-p, at least for now.

Thanks,

-- 
Basil

^ permalink raw reply related	[flat|nested] 100+ messages in thread
[parent not found: <<<87fu3vdjjk.fsf@tcd.ie>]
[parent not found: <<87fu3vdjjk.fsf@tcd.ie>]

end of thread, other threads:[~2019-04-22 15:19 UTC | newest]

Thread overview: 100+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-16 19:34 Predicate for true lists Basil L. Contovounesios
2018-06-04 12:12 ` Basil L. Contovounesios
2018-06-04 14:08   ` Stefan Monnier
2018-06-04 14:46     ` Basil L. Contovounesios
2018-06-04 15:31       ` Drew Adams
2018-06-04 16:14         ` Basil L. Contovounesios
2018-06-04 16:38           ` Drew Adams
2018-06-04 16:57             ` Stefan Monnier
2018-06-05  1:23   ` Paul Eggert
2018-06-05  2:57     ` Drew Adams
2018-06-05  3:08       ` Drew Adams
2018-06-05  3:12         ` Drew Adams
2018-06-05  3:25       ` Drew Adams
2018-06-05 15:05     ` Basil L. Contovounesios
2018-06-06  7:42       ` Paul Eggert
2018-06-06  9:40         ` Van L
2018-06-06 13:44           ` Stefan Monnier
2018-06-06 17:40             ` Stefan Monnier
2018-06-07  7:03             ` Van L
2018-07-05 22:31         ` Basil L. Contovounesios
2018-07-06  5:57           ` Eli Zaretskii
2018-07-06 17:16             ` Drew Adams
2018-07-06 17:38               ` Eli Zaretskii
     [not found]               ` <<83601sl0wo.fsf@gnu.org>
2018-07-06 18:00                 ` Drew Adams
2018-07-07  6:54                   ` Eli Zaretskii
     [not found]               ` <<<83601sl0wo.fsf@gnu.org>
     [not found]                 ` <<95fda70b-5893-4788-83c5-a0bb5d708304@default>
     [not found]                   ` <<8336wvleml.fsf@gnu.org>
2018-07-07 14:42                     ` Drew Adams
2018-07-06 18:04             ` Paul Eggert
2018-07-07  6:58               ` Eli Zaretskii
2018-07-07  7:20                 ` martin rudalics
2018-07-07  8:41                 ` Paul Eggert
2018-07-07 10:04                   ` Eli Zaretskii
2018-07-07 15:04                     ` Basil L. Contovounesios
2018-07-07 16:12                       ` Eli Zaretskii
2018-07-07 16:52                         ` Basil L. Contovounesios
2018-07-07 17:07                           ` Eli Zaretskii
2018-07-07 17:14                             ` Paul Eggert
2018-07-07 17:34                               ` Eli Zaretskii
2018-07-08  0:15                               ` Drew Adams
2018-07-08  4:48                                 ` Paul Eggert
2018-07-08 15:15                                   ` Drew Adams
2018-07-08 16:00                                     ` Paul Eggert
2018-07-08 17:42                                       ` Drew Adams
2018-07-08 17:47                                         ` Paul Eggert
2018-07-07 17:06                     ` Basil L. Contovounesios
2018-07-09 19:25             ` Basil L. Contovounesios
2018-07-09 19:40               ` Basil L. Contovounesios
2018-07-10  2:02                 ` Paul Eggert
2018-07-10  5:46                   ` Basil L. Contovounesios
2018-07-11  3:02                     ` Paul Eggert
2018-07-11  6:27                       ` Basil L. Contovounesios
2018-07-15 22:55                         ` Wilfred Hughes
2018-07-16  1:37                           ` Paul Eggert
2018-07-11 14:01                       ` [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el Karl Fogel
2018-07-11 17:12                         ` Basil L. Contovounesios
2018-07-11 17:33                           ` Paul Eggert
2018-07-12 15:34                             ` Basil L. Contovounesios
2018-07-12 15:43                               ` Basil L. Contovounesios
2019-04-09 12:51                       ` Predicate for true lists Basil L. Contovounesios
2019-04-09 15:33                         ` Stefan Monnier
2019-04-09 16:20                           ` Basil L. Contovounesios
2019-04-09 16:32                             ` Stefan Monnier
2019-04-09 16:54                               ` Daniel Colascione
2019-04-09 17:27                                 ` Basil L. Contovounesios
2019-04-09 17:27                               ` Basil L. Contovounesios
2019-04-09 20:08                               ` Unused value of error-free function warning (was: Predicate for true lists) Basil L. Contovounesios
2019-04-09 20:40                                 ` Unused value of error-free function warning Stefan Monnier
2019-04-09 20:12                           ` Predicate for true lists Basil L. Contovounesios
2019-04-09 20:41                             ` Stefan Monnier
2019-04-10  2:32                             ` Eli Zaretskii
2019-04-10 14:16                               ` Alex Branham
2019-04-10 14:34                                 ` Basil L. Contovounesios
2019-04-10 15:01                                   ` Drew Adams
2019-04-10 15:45                                     ` Basil L. Contovounesios
2019-04-10 16:04                                       ` Eli Zaretskii
2019-04-17 17:56                                       ` Basil L. Contovounesios
2019-04-17 18:11                                         ` Stefan Monnier
2019-04-21 21:42                                           ` Basil L. Contovounesios
2019-04-17 18:55                                         ` Drew Adams
2019-04-21 21:24                                           ` Basil L. Contovounesios
2019-04-22  0:03                                             ` Drew Adams
2019-04-22  1:12                                               ` Michael Heerdegen
2019-04-22  9:39                                                 ` Drew Adams
2019-04-18 14:37                                         ` Eli Zaretskii
2019-04-21 18:30                                           ` Basil L. Contovounesios
2019-04-21 19:39                                             ` Eli Zaretskii
2019-04-21 21:37                                               ` Basil L. Contovounesios
2019-04-22  0:06                                                 ` Drew Adams
2019-04-22  7:49                                                 ` Eli Zaretskii
2019-04-22 12:59                                                   ` Basil L. Contovounesios
2019-04-22 13:12                                                     ` Eli Zaretskii
2019-04-22 15:19                                                       ` Basil L. Contovounesios
2019-04-21 19:41                                             ` Eli Zaretskii
2019-04-21 21:41                                               ` Basil L. Contovounesios
2019-04-22  6:39                                                 ` Eli Zaretskii
2019-04-22 12:58                                                   ` Basil L. Contovounesios
2018-07-06 17:00           ` Drew Adams
2018-07-06 17:20             ` Paul Eggert
2018-07-06 17:33             ` Eli Zaretskii
2018-07-08 22:38             ` Basil L. Contovounesios
2018-07-06 17:30           ` Paul Eggert
     [not found] <<<87fu3vdjjk.fsf@tcd.ie>
     [not found] <<87fu3vdjjk.fsf@tcd.ie>

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).