emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 0f79046: Add new predicates for sequence lengths


From: Lars Ingebrigtsen
Subject: master 0f79046: Add new predicates for sequence lengths
Date: Sun, 27 Dec 2020 03:00:36 -0500 (EST)

branch: master
commit 0f790464d547dd57a857d88dab309b286067ac45
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add new predicates for sequence lengths
    
    * doc/lispref/sequences.texi (Sequence Functions): Document them.
    * lisp/emacs-lisp/byte-opt.el (side-effect-free-fns): Mark them as
    side-effect-free.
    
    * lisp/emacs-lisp/shortdoc.el (list): Mention them.
    
    * src/fns.c (Flength): Mention them in the doc string.
    (length_internal): New function.
    (Flength_less, Flength_greater, Flength_equal): New defuns.
    (syms_of_fns): Sym them.
---
 doc/lispref/sequences.texi  | 15 +++++++++
 etc/NEWS                    |  6 ++++
 lisp/emacs-lisp/byte-opt.el |  4 ++-
 lisp/emacs-lisp/shortdoc.el |  6 ++++
 src/fns.c                   | 76 ++++++++++++++++++++++++++++++++++++++++++++-
 test/src/fns-tests.el       | 30 ++++++++++++++++++
 6 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index 952834b..57b4984 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -116,6 +116,21 @@ If you need to compute the width of a string on display, 
you should use
 since @code{length} only counts the number of characters, but does not
 account for the display width of each character.
 
+@defun length< sequence length
+Return non-@code{nil} if @var{sequence} is shorter than @var{length}.
+This may be more efficient than computing the length of @var{sequence}
+if @var{sequence} is a long list.
+@end defun
+
+@defun length> sequence length
+Return non-@code{nil} if @var{sequence} is longer than @var{length}.
+@end defun
+
+@defun length= sequence length
+Return non-@code{nil} if the length of @var{sequence} is equal to
+@var{length}.
+@end defun
+
 @defun elt sequence index
 @anchor{Definition of elt}
 @cindex elements of sequences
diff --git a/etc/NEWS b/etc/NEWS
index d24d8b1..9ae8cc9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1460,6 +1460,12 @@ that makes it a valid button.
 
 ** Miscellaneous
 
++++
+*** New predicate functions 'length<', 'length>' and 'length='.
+Using these functions may be more efficient than using 'length' (if
+the length of a (long) list is being computed just to compare this
+length to a number).
+
 ---
 *** 'remove-hook' is now an interactive command.
 
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 469bbe6..0eee6e9 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -1169,7 +1169,9 @@
         hash-table-count
         int-to-string intern-soft isnan
         keymap-parent
-         lax-plist-get ldexp length line-beginning-position line-end-position
+         lax-plist-get ldexp
+         length length< length> length=
+         line-beginning-position line-end-position
         local-variable-if-set-p local-variable-p locale-info
         log log10 logand logb logcount logior lognot logxor lsh
         make-byte-code make-list make-string make-symbol marker-buffer max
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 9d183e0..c6259f8 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -618,6 +618,12 @@ There can be any number of :example/:result elements."
   "Data About Lists"
   (length
    :eval (length '(a b c)))
+  (length<
+   :eval (lenth< '(a b c) 1))
+  (length>
+   :eval (lenth> '(a b c) 1))
+  (length=
+   :eval (lenth> '(a b c) 3))
   (safe-length
    :eval (safe-length '(a b c))))
 
diff --git a/src/fns.c b/src/fns.c
index 646c3ed..0fded92 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -105,9 +105,14 @@ list_length (Lisp_Object list)
 DEFUN ("length", Flength, Slength, 1, 1, 0,
        doc: /* Return the length of vector, list or string SEQUENCE.
 A byte-code function object is also allowed.
+
 If the string contains multibyte characters, this is not necessarily
 the number of bytes in the string; it is the number of characters.
-To get the number of bytes, use `string-bytes'.  */)
+To get the number of bytes, use `string-bytes'.
+
+If the length of a list is being computed to compare to a (small)
+number, the `string<', `string>' and `string=' functions may be more
+efficient.  */)
   (Lisp_Object sequence)
 {
   EMACS_INT val;
@@ -145,6 +150,72 @@ least the number of distinct elements.  */)
   return make_fixnum (len);
 }
 
+static inline
+EMACS_INT length_internal (Lisp_Object sequence, int len)
+{
+  /* If LENGTH is short (arbitrarily chosen cut-off point), use a
+     fast loop that doesn't care about whether SEQUENCE is
+     circular or not. */
+  if (len < 0xffff)
+    while (CONSP (sequence))
+      {
+       if (--len == 0)
+         return -1;
+       sequence = XCDR (sequence);
+      }
+  /* Signal an error on circular lists. */
+  else
+    FOR_EACH_TAIL (sequence)
+      if (--len == 0)
+       return -1;
+  return len;
+}
+
+DEFUN ("length<", Flength_less, Slength_less, 2, 2, 0,
+       doc: /* Return non-nil if SEQUENCE is shorter than LENGTH.
+See `length' for allowed values of SEQUENCE and how elements are
+counted.  */)
+  (Lisp_Object sequence, Lisp_Object length)
+{
+  CHECK_FIXNUM (length);
+  EMACS_INT len = XFIXNUM (length);
+
+  if (CONSP (sequence))
+    return length_internal (sequence, len) == -1? Qnil: Qt;
+  else
+    return XFIXNUM (Flength (sequence)) < len? Qt: Qnil;
+}
+
+DEFUN ("length>", Flength_greater, Slength_greater, 2, 2, 0,
+       doc: /* Return non-nil if SEQUENCE is longer than LENGTH.
+See `length' for allowed values of SEQUENCE and how elements are
+counted.  */)
+  (Lisp_Object sequence, Lisp_Object length)
+{
+  CHECK_FIXNUM (length);
+  EMACS_INT len = XFIXNUM (length);
+
+  if (CONSP (sequence))
+    return length_internal (sequence, len + 1) == -1? Qt: Qnil;
+  else
+    return XFIXNUM (Flength (sequence)) > len? Qt: Qnil;
+}
+
+DEFUN ("length=", Flength_equal, Slength_equal, 2, 2, 0,
+       doc: /* Return non-nil if SEQUENCE is equal to LENGTH.
+See `length' for allowed values of SEQUENCE and how elements are
+counted.  */)
+  (Lisp_Object sequence, Lisp_Object length)
+{
+  CHECK_FIXNUM (length);
+  EMACS_INT len = XFIXNUM (length);
+
+  if (CONSP (sequence))
+    return length_internal (sequence, len + 1) == 1? Qt: Qnil;
+  else
+    return XFIXNUM (Flength (sequence)) == len? Qt: Qnil;
+}
+
 DEFUN ("proper-list-p", Fproper_list_p, Sproper_list_p, 1, 1, 0,
        doc: /* Return OBJECT's length if it is a proper list, nil otherwise.
 A proper list is neither circular nor dotted (i.e., its last cdr is nil).  */
@@ -5721,6 +5792,9 @@ this variable.  */);
   defsubr (&Srandom);
   defsubr (&Slength);
   defsubr (&Ssafe_length);
+  defsubr (&Slength_less);
+  defsubr (&Slength_greater);
+  defsubr (&Slength_equal);
   defsubr (&Sproper_list_p);
   defsubr (&Sstring_bytes);
   defsubr (&Sstring_distance);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index eaa569e..3486c74 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -999,3 +999,33 @@
              (object-intervals (current-buffer)))
            '((0 1 (foo 1)) (1 2 (zot 3 foo 1)) (2 4 (zot 3 bar 2))
              (4 5 (bar 2)) (5 6 nil)))))
+
+(ert-deftest length-equals-tests ()
+  (should-not (length< (list 1 2 3) 2))
+  (should-not (length< (list 1 2 3) 3))
+  (should (length< (list 1 2 3) 4))
+
+  (should-not (length< "abc" 2))
+  (should-not (length< "abc" 3))
+  (should (length< "abc" 4))
+
+  (should (length> (list 1 2 3) 2))
+  (should-not (length> (list 1 2 3) 3))
+  (should-not (length> (list 1 2 3) 4))
+
+  (should (length> "abc" 2))
+  (should-not (length> "abc" 3))
+  (should-not (length> "abc" 4))
+
+  (should-not (length= (list 1 2 3) 2))
+  (should (length= (list 1 2 3) 3))
+  (should-not (length= (list 1 2 3) 4))
+
+  (should-not (length= "abc" 2))
+  (should (length= "abc" 3))
+  (should-not (length= "abc" 4))
+
+  (should-error
+   (let ((list (list 1)))
+     (setcdr list list)
+     (length< list #x1fffe))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]