coding armax functionality each time from scratch have bored me to hell, so
i've decided to make a macro for this. i.e. simple case:
CL-USER> (with-find-optimal (check >)
(loop for i from 1 to 100
do (check i)))
100
100
with key being a function:
CL-USER> (with-find-optimal (check > -)
(loop for i from 1 to 100
do (check (cons i i))))
1
-1
with key being custom code:
CL-USER> (with-find-optimal (check >
(lambda (cons) (* (car cons) (cdr cons))))
(loop for i from 1 to 100
do (check (cons i i))))
(100 . 100)
10000
i suspect i'm not the only one doing such macro, but i'm not able to find
any of them, and don't even know how it could be called..
something tells me that fancy LOOP substitutions like ITERATE have this
built-in, but it would be overkill to use ITERATE just for this thing.
also, with my macro CHECK is lexical function, so it can be used in fairly
complex cases -- passed to other functions etc.
here's my code. i don't really like it (nor i like interface), but it seems
to work..
(defmacro with-find-optimal ((check compare &optional key) &body body)
(unless key (setf key 'identity))
(with-gensyms (option optimal optimal-value initialized option-value)
`(let (,optimal ,optimal-value ,initialized)
(flet ((,check (,option)
(let ((,option-value (,key ,option)))
(if ,initialized
(when (,compare ,option-value ,optimal-value)
(setf ,optimal ,option
,optimal-value ,option-value))
(setf ,optimal ,option
,optimal-value ,option-value
,initialized t)))))
,@body
(values ,optimal ,optimal-value)))))
Den Thu, 28 Feb 2008 20:22:33 +0200 skrev Alex Mizrahi:
> i suspect i'm not the only one doing such macro, but i'm not able to
> find any of them, and don't even know how it could be called.. something
> tells me that fancy LOOP substitutions like ITERATE have this built-in,
> but it would be overkill to use ITERATE just for this thing. also, with
> my macro CHECK is lexical function, so it can be used in fairly complex
> cases -- passed to other functions etc.
Indeed, ITERATE has MAXIMIZING/MINIMIZING clauses just for that. I
wouldn't say that switching is an overkill -- I find ITERATE much more
readable (thanks to more lispy syntax), and in general, a bit more
predictable than LOOP. It's also extensible, so you could add a new
clause, say, OPTIMIZING expr BY, so that it'd work like all built-in
clauses.
Cheers,
Maciej
"Alex Mizrahi" <········@users.sourceforge.net> writes:
> coding armax functionality each time from scratch have bored me to hell, so
> i've decided to make a macro for this. i.e. simple case:
>
.
.
.
> CL-USER> (with-find-optimal (check > -)
> (loop for i from 1 to 100
> do (check (cons i i))))
> 1
> -1
This example doesn't work
Argument N is not a NUMBER: (1 . 1).
[Condition of type SIMPLE-TYPE-ERROR]
> here's my code. i don't really like it (nor i like interface), but it seems
> to work..
I agree about the interface. It feels much more natural to
me to have the function take two arguments, the arg and the
value eg
(defmacro arg-max ((name &optional (test '>) (key 'identity)) &body code)
(with-gensyms (initialized best-value-so-far
best-arg-so-far)
`(let (,initialized
,best-value-so-far
,best-arg-so-far)
(flet ((,name (arg value)
(if ,initialized
(when (,test (,key value)
(,key ,best-value-so-far))
(setf ,best-value-so-far value
,best-arg-so-far arg))
(setf ,initialized t
,best-value-so-far value
,best-arg-so-far arg))))
,@code
(unless ,initialized
(error "arg-max of empty set"))
(values ,best-arg-so-far
,best-value-so-far)))))
CL-USER> (arg-max (hunt-pi <)
(loop for x from 2.5l0 to 3.5 by 0.001 do (hunt-pi x (cos x))))
3.1420000304933637d0
-0.9999999170220304d0
Alan Crowe
Edinburgh
Scotland