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]>
Eli Zaretskii <[email protected]> writes:

>> From: Philip Kaludercic <[email protected]>
>> Date: Sat, 08 Aug 2026 19:59:57 +0000
>> 
>> -(cl-defgeneric seq-min (sequence)
>> +(cl-defgeneric seq-min (sequence &optional lessp)
>>    "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 optional parameter LESSP, which
>> +defaults to `value<'."
>> +  (unless lessp (setq lessp #'value<))
>> +  (let ((fresh (eval-when-compile (make-symbol "fresh"))))
>> +    (seq-reduce
>> +     (lambda (acc elt)
>> +       (cond
>> +        ((eq acc fresh) elt)
>> +        ((funcall lessp acc elt) acc)
>> +        (t elt)))
>> +     sequence
>> +     fresh)))
>
> How about optimizing for the default nil value of LESSP?  The original
> implementation should be faster than the modified one, so how about
> keeping the original performance for those who don't need a fancy
> comparison function?

To know how much this matters or not, here are some benchmarks;

  (let ((list (make-list 10000 nil)))
    (dotimes (i (length list))
      (setf (nth i list) (random)))
    (benchmark-run 100
      (seq-min list)))

  ;; old implementation
  (0.007135554 0 0.0)

  ;; new implementation
  (0.957282933 8 0.7767616650000093)

even if the sequence type is not a list, there is a difference, though
less pronounced:

  (let ((vec (make-vector 10000 nil)))
    (dotimes (i (length vec))
      (setf (aref vec i) (random)))
    (benchmark-run 100
      (seq-min-old vec)))

  ;; old
  (0.108933372 1 0.09555754100000513)

  ;; new
  (0.891958548 7 0.7179179070000146)

Of course, the performance difference ultimately boils down to the fact
that `min' (and `max') is built-in, but `min' only operates on numbers
and markers.  Unless we implement a `min' and `max' that operate on
`value<' in C -- which I think would also be useful -- then to optimize
we would have to also change the fallback of LESSP to < and retain the
restriction that SEQUENCE has to consist of numerical values.

My counter-argument is that seq has never been performance oriented, as
it uses dynamic dispatch and is non-destructive most of the time.  There
is no advantage of using `seq-min' compared to `min' if you are dealing
with lists.  With other sequences you can avoid having to call
`seq-into' by hand.  So I would advocate to have seq be a more
interesting function instead of just a convenience wrapper, even if that
comes at the cost of a slowdown.

Or did I misunderstand you, and you are advocating for a case
distinction on LESSP, and dispatch to `min' and `max' if LESSP is `<`
(instead of nil)?
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.