[cl-utilities-devel] making EXTREMUM return multiple values -- and request for a function EXTREMA.

Tobias C. Rittweiler tcr at freebits.de
Tue Nov 15 15:42:07 UTC 2005


Hi Peter,

I'd like to suggest making EXTREMUM return all /equivalent/ extrema as
multiple values, such that for instance:
 
  CL-USER> (extremum '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr)
  (D . 1)
  (B . 1)

My more restrictive (featurewise) version based on parts of your code
but that I hacked together to fit my particular needs (no :start, :end
keywords, only works for lists), looks like this:

  ;;; Inspired by www.cliki.net/EXTREMUM), but this function returns
  ;;; all extrema of sequence (if being equal) as multiple values.
  (defun extremum (sequence predicate &key (key #'identity))
    (let* ((smallest-elements (list (first sequence)))
           (smallest-key (funcall key (first smallest-elements))))
      (map nil
           #'(lambda (x)
               (let ((x-key (funcall key x)))
                 (cond ((funcall predicate x-key smallest-key)
                        (setq smallest-elements (list x))
                        (setq smallest-key x-key))
                       ;; both elements are considered equal if the predicate
                       ;; returns false for (PRED A B) and (PRED B A)
                       ((not (funcall predicate smallest-key x-key))
                        (push x smallest-elements)))))
           (rest sequence))
      (apply #'values smallest-elements)))

Similiarly, I'd like to suggest a new function EXTREMA which returns the
N "topmost" extrema:

  CL-USER> (extrema 1 '(3 1 2 1) #'>)
  (3)
  CL-USER> (extrema 2 '(3 1 2 1) #'>)
  (3 2)
  CL-USER> (extrema 2 '(3 1 2 1) #'<)
  (1 1)
     
  CL-USER> (extrema 1 '((A . 3) (B . 1) (C . 2) (D . 1)) #'> :key #'cdr)
  ((A . 3))
  CL-USER> (extrema 2 '((A . 3) (B . 1) (C . 2) (D . 1)) #'< :key #'cdr)
  ((D . 1) (B . 1))

My version -- which can almost certainly be written many times
simpler -- is:

  (defun push-rotate-chop (item array &key (start 0) (end (length array)))
    (loop
       with saved-item = item
       for i from start to (1- end)
       do (rotatef saved-item (aref array i))))

  ; CL-USER> (let ((array (make-array 5 :initial-contents '(1 2 3 4 5))))
  ;            (push-rotate-chop 'a array) array)
  ; #(A 1 2 3 4)

  ; CL-USER> (let ((array (make-array 5 :initial-contents '(1 2 3 4 5))))
  ;            (push-rotate-chop 'a array :start 1 :end 4) array)
  ; #(1 A 2 3 5)

  (defun extrema (n list predicate &key (key #'identity))
    (let ((smallest-elements (make-array n))
          (smallest-keys (make-array n))
          (real-length 1))
      (flet ((free-slot-p (x) (not x)))
        (setf (aref smallest-elements 0) (first list)
              (aref smallest-keys 0) (funcall key (first list)))    
        (map nil
             #'(lambda (x)
                 (let ((x-key (funcall key x)))
                   (loop
                      for key-idx from 0
                      for key across smallest-keys do
                      (when (or (free-slot-p key)
                                (funcall predicate x-key key)        ; x-key < key
                                (not (funcall predicate key x-key))) ; x-key = key
                        (when (< real-length n) (incf real-length))
                        (push-rotate-chop x-key smallest-keys
                                          :start key-idx :end real-length)
                        (push-rotate-chop x smallest-elements
                                          :start key-idx :end real-length)
                        (loop-finish)))))
             (rest list))
        (coerce (subseq smallest-elements 0 real-length) 'list))))


Well, you'll hopefully get inspired. :-)


-t





More information about the cl-utilities-devel mailing list