From d1b3eb7eec64ffb9f2d89efda21660cab92bcf0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Fri, 10 Oct 2025 15:39:15 +0200 Subject: [PATCH] Add any and all (bug#79611) * lisp/subr.el (all, any): New. * test/lisp/subr-tests.el (subr-all, subr-any): New tests. * doc/lispref/lists.texi (List Elements): Document. * etc/NEWS: Announce. --- doc/lispref/lists.texi | 27 +++++++++++++++++++++++++++ etc/NEWS | 5 +++++ lisp/subr.el | 14 ++++++++++++++ test/lisp/subr-tests.el | 29 +++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index da6d167c740..37ef8d46525 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -415,6 +415,33 @@ will return a list equal to @var{list}. @end example @end defun +@defun all pred list +This function returns @code{t} if @var{pred} is true for all elements in +@var{list}. + +@example +@group +(all #'numberp '(1 2 3 4)) @result{} t +(all #'numberp '(1 2 a b 3 4)) @result{} nil +(all #'numberp '()) @result{} t +@end group +@end example +@end defun + +@defun any pred list +This function returns non-@code{nil} if @var{pred} is true for at least +one element in @var{list}. The returned value is the longest @var{list} +suffix whose first element satisfies @var{pred}. + +@example +@group +(any #'symbolp '(1 2 3 4)) @result{} nil +(any #'symbolp '(1 2 a b 3 4)) @result{} (a b 3 4) +(any #'symbolp '()) @result{} nil +@end group +@end example +@end defun + @defun last list &optional n This function returns the last link of @var{list}. The @code{car} of this link is the list's last element. If @var{list} is null, diff --git a/etc/NEWS b/etc/NEWS index cf608578c34..94a01a63649 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3142,6 +3142,11 @@ signal an error if they are given a non-integer. ** New functions 'drop-while' and 'take-while'. These work like 'drop' and 'take' but use a predicate instead of counting. ++++ +** New functions 'any' and 'all'. +These return non-nil for lists where any and all elements, respectively, +satisfy a given predicate. + +++ ** The 'defvar-local' macro second argument is now optional. This means that you can now call it with just one argument, like diff --git a/lisp/subr.el b/lisp/subr.el index 403e9dac376..216ad5eb4ab 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -1176,6 +1176,20 @@ side-effects, and the argument LIST is not modified." (while (and list (funcall pred (car list))) (setq list (cdr list))) list) + +(defun all (pred list) + "Non-nil if PRED is true for all elements in LIST." + (declare (compiler-macro (lambda (_) `(not (drop-while ,pred ,list))))) + (not (drop-while pred list))) + +(defun any (pred list) + "Non-nil if PRED is true for at least one element in LIST. +Returns the LIST suffix starting at the first element that satisfies PRED, +or nil if none does." + (declare (compiler-macro + (lambda (_) + `(drop-while (lambda (x) (not (funcall ,pred x))) ,list)))) + (drop-while (lambda (x) (not (funcall pred x))) list)) ;;;; Keymap support. diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index ae5932d96b3..fc980eae596 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -1545,5 +1545,34 @@ final or penultimate step during initialization.")) (should (equal (funcall (subr--identity #'take-while) #'plusp ls) '(3 2 1))))) +(ert-deftest subr-all () + (should (equal (all #'hash-table-p nil) t)) + (let ((ls (append '(3 2 1) '(0) '(-1 -2 -3)))) + (should (equal (all #'numberp ls) t)) + (should (equal (all (lambda (x) (numberp x)) ls) t)) + (should (equal (all #'plusp ls) nil)) + (should (equal (all #'bufferp ls) nil)) + (let ((z 9)) + (should (equal (all (lambda (x) (< x z)) ls) t)) + (should (equal (all (lambda (x) (> x (- z 9))) ls) nil)) + (should (equal (all (lambda (x) (> x z)) ls) nil))) + (should (equal (funcall (subr--identity #'all) #'plusp ls) nil)) + (should (equal (funcall (subr--identity #'all) #'numberp ls) t)))) + +(ert-deftest subr-any () + (should (equal (any #'hash-table-p nil) nil)) + (let ((ls (append '(3 2 1) '(0) '(-1 -2 -3)))) + (should (equal (any #'numberp ls) ls)) + (should (equal (any (lambda (x) (numberp x)) ls) ls)) + (should (equal (any #'plusp ls) ls)) + (should (equal (any #'zerop ls) '(0 -1 -2 -3))) + (should (equal (any #'bufferp ls) nil)) + (let ((z 9)) + (should (equal (any (lambda (x) (< x z)) ls) ls)) + (should (equal (any (lambda (x) (< x (- z 9))) ls) '(-1 -2 -3))) + (should (equal (any (lambda (x) (> x z)) ls) nil))) + (should (equal (funcall (subr--identity #'any) #'minusp ls) '(-1 -2 -3))) + (should (equal (funcall (subr--identity #'any) #'stringp ls) nil)))) + (provide 'subr-tests) ;;; subr-tests.el ends here