From: Lawrence Troxler
Subject: max question
Date: 
Message-ID: <65d6i6$eia$1@mycroft.westnet.com>
Is there any Common Lisp function that works like max, except that allow
supplying of a lambda expression to be mapped over the arguments before
the max is computed? For example, I have

(setq foo '((a 4) (b 5) (c 1))

and I would like to be able to do something like:

(max-list #'second foo) => (b 5)

This would be easy, except that I want to return the original object, and
not just the maximum value after the function is mapped on the list.

I can write such a function "from scratch", I suppose, but since this
seems like it must be a very common problem, I thought I'd post this to
see if there is a built in way to do this.

Larry


-- 
--  Larry Troxler  --  ··@westnet.com  --  Patterson, NY USA  --
  

From: Marco Antoniotti
Subject: Re: max question
Date: 
Message-ID: <scfbtz9pya1.fsf@ferrari.PATH.Berkeley.EDU>
In article <············@mycroft.westnet.com> Lawrence Troxler <··@westnet.com> writes:

> From: Lawrence Troxler <··@westnet.com>
> Newsgroups: comp.lang.lisp
> Date: 25 Nov 1997 00:36:22 GMT
> Organization: WestNet Internet Services
> Lines: 23
> 
> Is there any Common Lisp function that works like max, except that allow
> supplying of a lambda expression to be mapped over the arguments before
> the max is computed? For example, I have
> 
> (setq foo '((a 4) (b 5) (c 1))
> 
> and I would like to be able to do something like:
> 
> (max-list #'second foo) => (b 5)
> 
> This would be easy, except that I want to return the original object, and
> not just the maximum value after the function is mapped on the list.
> 
> I can write such a function "from scratch", I suppose, but since this
> seems like it must be a very common problem, I thought I'd post this to
> see if there is a built in way to do this.
> 

(defun max* (key &rest els)
   (when els
     (let ((maximum (first els)))
        (loop for el in (rest els)
              when (> (funcall key el) (funcall key maximum))
              do (setf maximum el)
              finally (return-from max* maximum)))))
-- 
Marco Antoniotti
==============================================================================
California Path Program - UC Berkeley
Richmond Field Station
tel. +1 - 510 - 231 9472
From: Lawrence Troxler
Subject: Re: max question
Date: 
Message-ID: <65dcuu$ikg$1@mycroft.westnet.com>
Marco Antoniotti <·······@ferrari.PATH.Berkeley.EDU> wrote:
: > I can write such a function "from scratch", I suppose, but since this
: > seems like it must be a very common problem, I thought I'd post this to
: > see if there is a built in way to do this.
: > 

: (defun max* (key &rest els)
:    (when els
:      (let ((maximum (first els)))
:         (loop for el in (rest els)
:               when (> (funcall key el) (funcall key maximum))
:               do (setf maximum el)
:               finally (return-from max* maximum)))))

So I guess this means the answer is, no, there is no standard CL way do do
this :-)

--  Larry Troxler  --  ··@westnet.com  --  Patterson, NY USA  --
  
From: Kent M Pitman
Subject: Re: max question
Date: 
Message-ID: <sfw7m9x7ka4.fsf@world.std.com>
Lawrence Troxler <··@westnet.com> writes:

> Marco Antoniotti <·······@ferrari.PATH.Berkeley.EDU> wrote:
> : > I can write such a function "from scratch", I suppose, but since this
> : > seems like it must be a very common problem, I thought I'd post this to
> : > see if there is a built in way to do this.
> : > 
> 
> : (defun max* (key &rest els)
> :    (when els
> :      (let ((maximum (first els)))
> :         (loop for el in (rest els)
> :               when (> (funcall key el) (funcall key maximum))
> :               do (setf maximum el)
> :               finally (return-from max* maximum)))))
> 
> So I guess this means the answer is, no, there is no standard CL way do do
> this :-)

Well, you're overlooking REDUCE, which is a latter-day invention of
considerable and oft-ignored power.

 (let ((foo '((a 4) (b 5) (c 1))))
   (reduce #'(lambda (x y) (if (> (second x) (second y)) x y))
           foo))
 => (B 5)

Note that REDUCE doesn't separate the notion of the predicate arg from the
notion of what to accumulate, so you can't just do what might look obvious
which is:

 (let ((foo '((a 4) (b 5) (c 1))))
   (reduce #'max foo :key #'second))
 because that returns 5

You could do

 (defun pair-max (x y) (if (> (second x) (second y)) x y))

and then

 (let ((foo '((a 4) (b 5) (c 1))))
   (reduce #'pair-max foo)) => (B 5)

Then again, the fact that there is a functional way doesn't mean it's the CL
way.  A lot of CL users don't like the functional approach and prefer LOOP
anyway.

Btw, because MAX has no one-argument form, it's safer to give an :initial-value
with REDUCE unless you know FOO will be non-null. e.g., consider:

 (reduce #'pair-max '())
 Error: PAIR-MAX got 0 args, wanted at least 2.

 (reduce #'pair-max '() :initial-value '(zap 0))
 => (ZAP 0)
From: Marco Antoniotti
Subject: Re: max question
Date: 
Message-ID: <scf4t5154np.fsf@ferrari.PATH.Berkeley.EDU>
Kent M Pitman <······@world.std.com> writes:

> 
> 
> Well, you're overlooking REDUCE, which is a latter-day invention of
> considerable and oft-ignored power.
> 
>  (let ((foo '((a 4) (b 5) (c 1))))
>    (reduce #'(lambda (x y) (if (> (second x) (second y)) x y))
>            foo))
>  => (B 5)
> 
> Note that REDUCE doesn't separate the notion of the predicate arg from the
> notion of what to accumulate, so you can't just do what might look obvious
> which is:
> 
>  (let ((foo '((a 4) (b 5) (c 1))))
>    (reduce #'max foo :key #'second))
>  because that returns 5
> 
	...
> 
> Then again, the fact that there is a functional way doesn't mean it's the CL
> way.  A lot of CL users don't like the functional approach and prefer LOOP
> anyway.
> 

As a matter of fact, I did consider REDUCE (much more elegant!) but
working in C for sucha a long time has brain-damaged me. :)

-- 
Marco Antoniotti
==============================================================================
California Path Program - UC Berkeley
Richmond Field Station
tel. +1 - 510 - 231 9472
From: Martin Rodgers
Subject: Re: max question
Date: 
Message-ID: <MPG.ee6836f1888329b989795@news.demon.co.uk>
Marco Antoniotti wheezed these wise words:

> As a matter of fact, I did consider REDUCE (much more elegant!) but
> working in C for sucha a long time has brain-damaged me. :)

Back in the 80s, when I did much more C coding than I do now, I used 
to love REDUCE. Even after discovering LOOP, I still like it.
-- 
Please note: my email address is munged; You can never browse enough
                  "Oh knackers!" - Mark Radcliffe
From: Lawrence Troxler
Subject: Re: max question
Date: 
Message-ID: <65ij3a$e78$1@mycroft.westnet.com>
: Please note: my email address is munged; You can never browse enough

Please don't "munge". I always use my real email when posting, and it hasp
proven to be not a problem: I receive at most about 10 spams per day, and
I'm  a regular ng poster.

Larry
 -- 
--  Larry Troxler  --  ··@westnet.com  --  Patterson, NY USA  --
  
From: Hank Heijink
Subject: Re: max question
Date: 
Message-ID: <heijink-2611970945170001@socsci140.socsci.kun.nl>
Lawrence Troxler <··@westnet.com> wrote:

> Is there any Common Lisp function that works like max, except that allow
> supplying of a lambda expression to be mapped over the arguments before
> the max is computed? For example, I have
> 
> (setq foo '((a 4) (b 5) (c 1))
> 
> and I would like to be able to do something like:
> 
> (max-list #'second foo) => (b 5)

[snip]

> I can write such a function "from scratch", I suppose, but since this
> seems like it must be a very common problem, I thought I'd post this to
> see if there is a built in way to do this.

One (inefficient) way is:

(defun max-list (key lst)
  (first (sort lst #'> :key key)))

Since you only want the maximum value it seems pointless to sort the whole
list. There are other ways of combining functions, but I think it's
easiest to just write the thing yourself, for example:

(defun max-list (key lst)
  (loop with maximum = (first lst)
        for item in lst
        when (> (funcall key item) (funcall key maximum))
        do (setf maximum item)
        finally return maximum))

What would you want to return if the argument were '((a 4) (b 5) (d 5) (c
1))? If you would use standard lisp functions like sort and max, you could
get either (b 5) or (d 5), depending on their implementation. According to
CLtL they are free to do whatever would be easiest to implement.

Good luck!

Hank