1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-29 08:31:35 -08:00

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.
This commit is contained in:
Lars Ingebrigtsen 2020-12-27 09:00:23 +01:00
parent 714ca849ba
commit 0f790464d5
6 changed files with 135 additions and 2 deletions

View file

@ -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 since @code{length} only counts the number of characters, but does not
account for the display width of each character. 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 @defun elt sequence index
@anchor{Definition of elt} @anchor{Definition of elt}
@cindex elements of sequences @cindex elements of sequences

View file

@ -1460,6 +1460,12 @@ that makes it a valid button.
** Miscellaneous ** 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. *** 'remove-hook' is now an interactive command.

View file

@ -1169,7 +1169,9 @@
hash-table-count hash-table-count
int-to-string intern-soft isnan int-to-string intern-soft isnan
keymap-parent 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 local-variable-if-set-p local-variable-p locale-info
log log10 logand logb logcount logior lognot logxor lsh log log10 logand logb logcount logior lognot logxor lsh
make-byte-code make-list make-string make-symbol marker-buffer max make-byte-code make-list make-string make-symbol marker-buffer max

View file

@ -618,6 +618,12 @@ There can be any number of :example/:result elements."
"Data About Lists" "Data About Lists"
(length (length
:eval (length '(a b c))) :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 (safe-length
:eval (safe-length '(a b c)))) :eval (safe-length '(a b c))))

View file

@ -105,9 +105,14 @@ list_length (Lisp_Object list)
DEFUN ("length", Flength, Slength, 1, 1, 0, DEFUN ("length", Flength, Slength, 1, 1, 0,
doc: /* Return the length of vector, list or string SEQUENCE. doc: /* Return the length of vector, list or string SEQUENCE.
A byte-code function object is also allowed. A byte-code function object is also allowed.
If the string contains multibyte characters, this is not necessarily If the string contains multibyte characters, this is not necessarily
the number of bytes in the string; it is the number of characters. 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) (Lisp_Object sequence)
{ {
EMACS_INT val; EMACS_INT val;
@ -145,6 +150,72 @@ least the number of distinct elements. */)
return make_fixnum (len); 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, 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. 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). */ A proper list is neither circular nor dotted (i.e., its last cdr is nil). */
@ -5721,6 +5792,9 @@ this variable. */);
defsubr (&Srandom); defsubr (&Srandom);
defsubr (&Slength); defsubr (&Slength);
defsubr (&Ssafe_length); defsubr (&Ssafe_length);
defsubr (&Slength_less);
defsubr (&Slength_greater);
defsubr (&Slength_equal);
defsubr (&Sproper_list_p); defsubr (&Sproper_list_p);
defsubr (&Sstring_bytes); defsubr (&Sstring_bytes);
defsubr (&Sstring_distance); defsubr (&Sstring_distance);

View file

@ -999,3 +999,33 @@
(object-intervals (current-buffer))) (object-intervals (current-buffer)))
'((0 1 (foo 1)) (1 2 (zot 3 foo 1)) (2 4 (zot 3 bar 2)) '((0 1 (foo 1)) (1 2 (zot 3 foo 1)) (2 4 (zot 3 bar 2))
(4 5 (bar 2)) (5 6 nil))))) (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))))