From: larry
Subject: using loop macro to find argmax
Date: 
Message-ID: <7b8f89d6.0305121340.6094f1b4@posting.google.com>
Using the loop macro you can find the maximum of some numerical function f, applied
to each element of a list by 
(loop for x in list maxmimize (funcall f x))

But I want to return the x such that (funcall f x) is maximum
and I would like to write something like this:

(loop for x in list argmaximize (funcall f x))

Is there some loop keyword that would do this?
If there isn't such a keyword what's the most succinct way to
do this with the loop macro?

From: Barry Margolin
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <4OVva.33$293.1527@paloalto-snr1.gtei.net>
In article <····························@posting.google.com>,
larry <··········@hotmail.com> wrote:
>Using the loop macro you can find the maximum of some numerical function
>f, applied
>to each element of a list by 
>(loop for x in list maxmimize (funcall f x))
>
>But I want to return the x such that (funcall f x) is maximum
>and I would like to write something like this:
>
>(loop for x in list argmaximize (funcall f x))
>
>Is there some loop keyword that would do this?
>If there isn't such a keyword what's the most succinct way to
>do this with the loop macro?

There isn't a keyword for it.

(loop with max-val = (car list)
      for x in (cdr list)
      when (> (funcall f x) max-val)
      do (setq max-val x))

-- 
Barry Margolin, ··············@level3.com
Genuity Managed Services, a Level(3) Company, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Wade Humeniuk
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <XBWva.1541$3X2.750432@news0.telusplanet.net>
"larry" <··········@hotmail.com> wrote in message
·································@posting.google.com...
> Using the loop macro you can find the maximum of some numerical function f, applied
> to each element of a list by
> (loop for x in list maxmimize (funcall f x))
>
> But I want to return the x such that (funcall f x) is maximum
> and I would like to write something like this:
>
> (loop for x in list argmaximize (funcall f x))
>
> Is there some loop keyword that would do this?
> If there isn't such a keyword what's the most succinct way to
> do this with the loop macro?

(defun argmaximize (f list &optional (test #'>))
  (loop with maxarg = nil
        with maxfarg = nil
        with argposition = nil
        for position from 0
        for arg in list
        do
        (let ((fa (funcall f arg)))
          (when (or (null maxfarg)
                    (funcall test fa maxfarg))
            (setf maxarg arg maxfarg fa argposition position)))
        finally (return-from argmaximize (values maxarg maxfarg argposition))))

CL-USER 8 > (argmaximize #'1+ (list 12 78 0 98 102 6 7))
102
103
4

CL-USER 9 > (argmaximize #'1+ nil)
NIL
NIL
NIL

CL-USER 10 > (argmaximize #'1+ (list 12 78 0 98 102 6 7) #'<)
0
1
2

CL-USER 11 >

Wade
From: Jochen Schmidt
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <b9sug7$adj$03$1@news.t-online.com>
larry wrote:

> Using the loop macro you can find the maximum of some numerical function
> f, applied to each element of a list by
> (loop for x in list maxmimize (funcall f x))
> 
> But I want to return the x such that (funcall f x) is maximum
> and I would like to write something like this:
> 
> (loop for x in list argmaximize (funcall f x))
> 
> Is there some loop keyword that would do this?
> If there isn't such a keyword what's the most succinct way to
> do this with the loop macro?

It's not a loop clause but this should work:

(reduce (lambda (x y) (if (> (funcall f x)(funcall f y)) x y)) list))

ciao,
Jochen
From: Madhu
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <m3znlpu2c6.fsf@robolove.meer.net>
Helu

* Jochen Schmidt <···············@news.t-online.com> :
| larry wrote:
|> Using the loop macro you can find the maximum of some numerical function
|> f, applied to each element of a list by
|> (loop for x in list maxmimize (funcall f x))
|> 
|> But I want to return the x such that (funcall f x) is maximum
|> and I would like to write something like this:
|> 
|> (loop for x in list argmaximize (funcall f x))
|> 
|> Is there some loop keyword that would do this?
|> If there isn't such a keyword what's the most succinct way to
|> do this with the loop macro?

| It's not a loop clause but this should work:
| (reduce (lambda (x y) (if (> (funcall f x)(funcall f y)) x y)) list))

This may not be an issue but the above form funcalls f twice as many
times as required. Any attempts to eliminate redundant funcalls
results in forms like:

(let ((max-val (funcall f (car list))))
  (reduce #'(lambda (x y (&aux (fy (funcall f y))))
	      (if (> max-val fy)
		  x
		  (progn (setq max-val fy) y)))
	  list))

which arent really succinct. Barry's LOOP solution looks just as
good... which brings me to my real question:

While people still talking about new substandards for CL, I'd like to
see a proposal for an extensible LOOP.  How best could a new keyword
`argmaximize' be ingratiated into an extensible LOOP framework by a
user?


Regards
Madhu

-- 
Open system or closed system, enlightenment or ideology, those are the
questions.	         	    "John C. Mallery" <····@ai.mit>
From: Chris Riesbeck
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <riesbeck-A01A63.12014614052003@news.it.northwestern.edu>
In article <··············@robolove.meer.net>, Madhu <·······@meer.net> 
wrote:

>Any attempts to eliminate redundant funcalls
>results in forms like:
>
>(let ((max-val (funcall f (car list))))
>  (reduce #'(lambda (x y (&aux (fy (funcall f y))))
>	      (if (> max-val fy)
>		  x
>		  (progn (setq max-val fy) y)))
>	  list))
>
>which arent really succinct. Barry's LOOP solution looks just as
>good... which brings me to my real question:
>
>While people still talking about new substandards for CL, I'd like to
>see a proposal for an extensible LOOP.  How best could a new keyword
>`argmaximize' be ingratiated into an extensible LOOP framework by a
>user?

In pre-CL2 days, I defined extensible LOOP's that
were much simpler and more limited than CL's. Even
then, documenting how such extensions had to interact
with initialization, update, and termination clauses
was non-trivial.

I think there'd be better luck defining an extensible
WITH-COLLECTOR macro to define a collection function that 
could be called inside any looping construct.

One possible syntax, that emphasizes brevity of calling code:

  (with-collector name . body)

defines the function (name x) local to body that collects
values into a list. with-collector returns that list  on
exit.

  (with-collector (name reduce-fn init-val) . body)

defines (name x) local to body to use (reduce-fn old-value x)
to collect the value to return, where old-value is initially
init-val (NIL if not given).

Example calls:

  (with-collector collect
    (dotimes (i 5) (collect i)))

returns (0 1 2 3 4)

  (with-collector (maximize max 0)
    (dolist (n '(3 9 1 10 5)) (maximize n)))

returns 10.
From: Madhu
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <m3u1bxszwj.fsf@robolove.meer.net>
Helu

* Chris Riesbeck <······························@news.it.northwestern.edu> :
| In pre-CL2 days, I defined extensible LOOP's that
| were much simpler and more limited than CL's. Even
| then, documenting how such extensions had to interact
| with initialization, update, and termination clauses
| was non-trivial.

I can totally believe that the complexities here could undo
benefits from generalization.

| I think there'd be better luck defining an extensible
| WITH-COLLECTOR macro to define a collection function that 
| could be called inside any looping construct.
...
|   (with-collector (name reduce-fn init-val) . body)

| defines (name x) local to body to use (reduce-fn old-value x)
| to collect the value to return, where old-value is initially
| init-val (NIL if not given).

Yes. Also, in this case I think a bogus reduce-fn and CL's loop -

* (with-collector (arg-maximize #'(lambda (x c) x)
		     (car list)) ; init-val
     (loop for x in list
           for fx = (funcall f x)
	   maximizing fx into max-val
           unless (> max-val fx) 
           do (arg-maximize x)))

would collect what the OP wanted -- with CMUCL's COLLECT (and
LOOP) macros at least. (However, I can also see that it doesnt
really buy lots of expressiveness)


Regards
Madhu <:

--
From: Chris Riesbeck
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <riesbeck-A7DC1C.14141516052003@news.it.northwestern.edu>
In article <··············@robolove.meer.net>, Madhu <·······@meer.net> 
wrote:

>* Chris Riesbeck <······························@news.it.northwestern.edu> :
>
>| I think there'd be better luck defining an extensible
>| WITH-COLLECTOR macro to define a collection function that 
>| could be called inside any looping construct.
>...
>|   (with-collector (name reduce-fn init-val) . body)
>
>Yes. Also, in this case I think a bogus reduce-fn and CL's loop -
>
>* (with-collector (arg-maximize #'(lambda (x c) x)
>		     (car list)) ; init-val
>     (loop for x in list
>           for fx = (funcall f x)
>	   maximizing fx into max-val
>           unless (> max-val fx) 
>           do (arg-maximize x)))
>
>would collect what the OP wanted -- with CMUCL's COLLECT (and
>LOOP) macros at least. (However, I can also see that it doesnt
>really buy lots of expressiveness)

Um, well, yes, I suppose so, if you want to solve the original
problem, umph...

I currently would lean towards a with-maximizer similar
to but separate from a with-collector. The syntax could be

  (with-maximizer (name &key key test) . body)

where test defaults to > and key defaults to identity.
This defines (name x) local to body to save x when 

  (funcall test (funcall key x) (funcall key previously-saved-x))

is true, though key and test would be called at most once per x.
Then the original problem could be done with:

   (with-maximizer (maximize :key f)
     (loop for x in list
           do (maximize x)))

Other examples:

(flet ((square (x) (* x x)))
    (assert (= 10
               (with-maximizer (maximize)
                 (dolist (n '(3 9 1 10 5)) (maximize n)))))
    (assert (= 100
               (with-maximizer (maximize)
                 (dolist (n '(3 9 1 10 5)) (maximize (square n))))))
    (assert (= 10
               (with-maximizer (maximize :key #'square)
                 (dolist (n '(3 9 1 10 5)) (maximize n)))))
    (assert (= 1
               (with-maximizer (maximize :key #'/)
                 (dolist (n '(3 9 1 10 5)) (maximize n)))))
    (assert (= 1
               (with-maximizer (maximize :test #'<)
                 (dolist (n '(3 9 1 10 5)) (maximize n)))))
    (assert (= 10
               (with-maximizer (maximize :test #'< :key #'/)
                 (dolist (n '(3 9 1 10 5)) (maximize n)))))
    )
From: Madhu
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <m37k8plg1h.fsf@robolove.meer.net>
* Chris Riesbeck <······························@news.it.northwestern.edu> :
| In article <··············@robolove.meer.net>, Madhu <·······@meer.net> 
| wrote:
| 
| >* Chris Riesbeck <······························@news.it.northwestern.edu> :
| >
| >| I think there'd be better luck defining an extensible
| >| WITH-COLLECTOR macro to define a collection function that 
| >| could be called inside any looping construct.
| >...
| >|   (with-collector (name reduce-fn init-val) . body)
| >
| >Yes. Also, in this case I think a bogus reduce-fn and CL's loop -
| >
| >* (with-collector (arg-maximize #'(lambda (x c) x)
| >		     (car list)) ; init-val
| >     (loop for x in list
| >           for fx = (funcall f x)
| >	   maximizing fx into max-val
| >           unless (> max-val fx) 
| >           do (arg-maximize x)))
| >
| >would collect what the OP wanted -- with CMUCL's COLLECT (and
| >LOOP) macros at least. (However, I can also see that it doesnt
| >really buy lots of expressiveness)
| 
| Um, well, yes, I suppose so, if you want to solve the original
| problem, umph...
| 
| I currently would lean towards a with-maximizer similar
| to but separate from a with-collector. The syntax could be
| 
|   (with-maximizer (name &key key test) . body)
| 
| where test defaults to > and key defaults to identity.
| This defines (name x) local to body to save x when 
| 
|   (funcall test (funcall key x) (funcall key previously-saved-x))
| 
| is true, though key and test would be called at most once per x.
| Then the original problem could be done with:
| 
|    (with-maximizer (maximize :key f)
|      (loop for x in list
|            do (maximize x)))
| 
...

Thanks for your comments and examples. Much appreciated.

I agree that WITH-MAXIMIZER is what I'd use if such problems
cropped up often enough in my code.  

Actually I was just trying to come up with it might look like
expressed in LOOP; and if it would be worthwhile trying to hook
in new accumulation keywords into LOOP via WITH-COLLECTOR.

The common pattern that the original problem seems to motivate is
one where I'd want to KEEP a loop variable (conditionally) on a
test. The missing accumulation clause which would allow me to
express these problems within LOOP itself seems to be KEEP or
KEEPING. (This clause would accumulate a single value that is
computed as if by #'identity)

[Of course this is just a (wishful) thought-exercise on my part
around the "powerful" loop macro, which despite its many keywords
doesnt seem to be very popular, and is also unable to use them
alone to express all sequential control structures conceivable by
man <;]
From: Chris Riesbeck
Subject: Re: using loop macro to find argmax
Date: 
Message-ID: <riesbeck-C9C36F.09313019052003@news.it.northwestern.edu>
In article <··············@robolove.meer.net>, Madhu <·······@meer.net> 
wrote:

>Actually I was just trying to come up with it might look like
>expressed in LOOP; and if it would be worthwhile trying to hook
>in new accumulation keywords into LOOP via WITH-COLLECTOR.
>...
>
>[Of course this is just a (wishful) thought-exercise on my part
>around the "powerful" loop macro, which despite its many keywords
>doesnt seem to be very popular,

LOOP has detractors *because* not *despite* its many keywords. 
The keywords interact in complex ways, not all well-defined
in the standard. 

Personally, I think that LOOP lets you write more readable, 
more easily maintained code AS LONG AS you follow general 
principles of writing all code in short task-focused functions 
or methods. A LOOP with a ton of interacting clauses is bad,
but an equivalent block of mapping or other iterative code 
would be just as bad. 

For a lot of good discussion on LOOP, including the kind
of example that started this thread, search for the articles
titled 'the "loop" macro' on Google Groups.