Subject: Re: Am I missing something about (loop ... maximizing ...) ?
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 21 Mar 2006 22:27:45 -0600
Newsgroups: comp.lang.lisp
Message-ID: <uO6dncWP0rjcT73ZRVn-gw@speakeasy.net>
<jonrock@gmail.com> wrote:
+---------------
| I would like to find the entry in a list that maximizes a function.
| I started out with this:
|    (defun find-maximizing-item (lst fun)
|      (loop for item in lst
|            maximizing (funcall fun item)))
| but that returns only the maximum value of the function and nothing
| about what entry in the list made that maximum value happen.
| Is there some other idiom I should be using instead?
+---------------

I don't know of one. AFAIK, one has to do it "manually", e.g.:

    (defun find-maximizing-item (list &optional (function #'identity)
				      &key (test #'<))
      (let ((max-item (first list))
	    (max-value (funcall function (first list)))
	    (max-position 0))
	(loop for item in (rest list)
	      and position from 1
	      for value = (funcall function item)
	  when (funcall test max-value value)
	    do (setf max-item item
		     max-value value
		     max-position position))
	(values max-item max-value max-position)))

I added MAX-POSITION 'cuz it might be useful sometimes
[but made it be the last value in the result, in case
you don't care]; uncrunched the Schemish variable names;
made the FUNCTION optional; added TEST [just for fun];
and unrolled the loop once to avoid calling FUNCTION
twice on the first item.  Testing:

    > (find-maximizing-item '(3 -4 2 5 -7 1 2))

    5
    5
    3
    > (find-maximizing-item '(3 -4 2 5 -7 1 2) (lambda (x) (* x x)))

    -7
    49
    4
    > 

To make it more like other CL functions, you might want to make
the calling sequence use keyword KEY instead of a positional FUNCTION,
that is:

    (defun find-maximizing-item (list &key (key #'identity) (test #'<))
      (let ((max-item (first list))
	    (max-value (funcall key (first list)))
	    (max-position 0))
	(loop for item in (rest list)
	      and position from 1
	      for value = (funcall key item)
	  when (funcall test max-value value)
	    do (setf max-item item
		     max-value value
		     max-position position))
	(values max-item max-value max-position)))

    > (find-maximizing-item '(3 -4 2 5 -7 1 2) :key (lambda (x) (* x x)))

    -7
    49
    4
    > 


-Rob

p.s. I used #'< as the default :TEST [even though MAX-VALUE isn't an
input constant], because CLHS 17.2.1 "Satisfying a Two-Argument Test"
requires the list item being scanned to be the second argument of the
:TEST function.

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607