bug#81581: [PATCH 1/1] Add a LESSP argument to 'seq-min' and 'seq-max'

Philip Kaludercic <[email protected]>
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
Sean Whitton <[email protected]> writes:

> Philip Kaludercic [14/Aug  4:07pm GMT] wrote:
>> Sean Whitton <[email protected]> writes:
>>
>>> Philip Kaludercic [09/Aug  4:29pm GMT] wrote:
>>>>> Code looks good, interesting approach with the eval-when-compile
>>>>> (I would have used a cl-loop since it doesn't seem like it's truly a
>>>>> reduction, but what you have is fine).
>>>>
>>>> Why isn't this a reduction?
>>>
>>> Just a defeasible aesthetic intuition.
>>
>> My counter-argument is that min is a associative operation, and given a
>> non-empty list kind-of unital as well, so for me it fits the bill.
>>
>>>> Sean Whitton <[email protected]> writes:
>>>>
>>>>> Sean Whitton [09/Aug 11:50am +01] wrote:
>>>>>> Code looks good, interesting approach with the eval-when-compile
>>>>>> (I would have used a cl-loop since it doesn't seem like it's truly a
>>>>>> reduction, but what you have is fine).
>>>>>
>>>>> Well, a while loop, probably, given seq.el is preloaded.
>>>>
>>>> Hmm, but that would amount to just manually looping over the sequence
>>>> (seq-length seq) many times and comparing the current best element with
>>>> that in (seq-elt seq i)?
>>>
>>> Yes.
>>
>> I would vote against this, since when reducing over a list this would
>> require a O(n) access for every element of the list (O(n) again), while
>> seq-reduce, via seq-do calls mapc internally, that iterates over the
>> list directly.
>
> That's fine, I was just making a few comments.
>
> Using a keyword argument, though, I do think you should change.

Sure, I've made that change add added a :key keyword while at it.  Also
note there was a bug in the last version, where if the sequence is empty
it would return a symbol instead of raising an error:
0001-Add-a-LESSP-argument-to-seq-min-and-seq-max.patch (text/x-diff, 3 KB)
From 83d10a35ce274a5a10c3d925956923307589ac7e Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <[email protected]>
Date: Sat, 8 Aug 2026 21:56:57 +0200
Subject: [PATCH] Add a LESSP argument to 'seq-min' and 'seq-max'

* lisp/emacs-lisp/seq.el (seq-min, seq-max): Add an optional
argument allowing the generalization of sequence to more than
just numerical data.
---
 lisp/emacs-lisp/seq.el | 44 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 38 insertions(+), 6 deletions(-)

diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index b8f35c10def..b541adca003 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -604,16 +604,48 @@ seq-group-by
    (seq-reverse sequence)
    nil))
 
-(cl-defgeneric seq-min (sequence)
+(cl-defgeneric seq-min (sequence &key (key #'identity) (lessp #'value<))
   "Return the smallest element of SEQUENCE.
-SEQUENCE must be a sequence of numbers or markers."
-  (apply #'min (seq-into sequence 'list)))
+Values are compared according to the keyword argument LESSP, which
+defaults to `value<' but can otherwise be any two argument function that
+returns non-nil if the first argument is less than the second.  The
+keyword argument KEY may be a function that takes a single argument and
+returns the key value used in comparison.  If KEY is not specified, the
+elements of SEQUENCE are compared directly."
+  (let* ((fresh (eval-when-compile (make-symbol "fresh")))
+         (min (seq-reduce
+               (lambda (acc elt)
+                 (cond
+                  ((eq acc fresh) elt)
+                  ((funcall lessp (funcall key acc) (funcall key elt)) acc)
+                  (t elt)))
+               sequence
+               fresh)))
+    (when (eq min fresh)
+      (error "Cannot find minimal element of empty sequence"))
+    min))
 
 ;;;###autoload
-(cl-defgeneric seq-max (sequence)
+(cl-defgeneric seq-max (sequence &key (key #'identity) (lessp #'value<))
   "Return the largest element of SEQUENCE.
-SEQUENCE must be a sequence of numbers or markers."
-  (apply #'max (seq-into sequence 'list)))
+Values are compared according to the keyword argument LESSP, which
+defaults to `value<' but can otherwise be any two argument function that
+returns non-nil if the first argument is less than the second.  The
+keyword argument KEY may be a function that takes a single argument and
+returns the key value used in comparison.  If KEY is not specified, the
+elements of SEQUENCE are compared directly."
+  (let* ((fresh (eval-when-compile (make-symbol "fresh")))
+         (max (seq-reduce
+               (lambda (acc elt)
+                 (cond
+                  ((eq acc fresh) elt)
+                  ((funcall lessp (funcall key acc) (funcall key elt)) elt)
+                  (t elt)))
+               sequence
+               fresh)))
+    (when (eq max fresh)
+      (error "Cannot find maximal element of empty sequence"))
+    max))
 
 (defun seq--count-successive (pred sequence)
   "Count successive elements in SEQUENCE for which PRED returns non-nil."
-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.