Hello,
I'm coding a genetic programming library in Lisp. I have a doubt trying
to separate the genetic algorithm code from the code that outputs the
results.
I want to be able to use different loggers: log to screen, log to a
database, log to files... Ideally I would be able to use many of these
at the same time; but that's not really critical, just desirable.
I need different functions (or methods, that's part of my question) to
log experiment arguments, runs (an experiment will almost always consist
of several runs), and population measurements (mean fitness, max
fitness...).
As I see it I have several options:
1) Hardcode the logging code into the genetic programming code. This
would be a PITA to change afterward. So I discard this one.
2) Pass the logging functions as arguments to the GP code. I don't like
this solution neither. The GP code already requires a long list of
parameters. And the calls to the logging functions would obscure the GP
code.
3) Use functions that redefine the GP functions so they call the logging
functions before and after the GP code is executed. This looks to me
like a nasty hack and feels like I would be reinventing the wheel
(:before and :after methods). And it would make rather difficult to
change the logging method once it is established (I could save the
original function in the property list, but that seems even nastier).
4) Use :before and :after methods. This option looks elegant. BUT I
don't know how I could use several logging methods at the same time.
I've made some experiments using the REPL and it seems I can only have
only one :before and only one :after method for a given method.
CL-USER> (defmethod foo ((a symbol) (b fixnum))
(format t "~A~A" a b))
STYLE-WARNING: implicitly creating new generic function FOO
#<STANDARD-METHOD FOO (SYMBOL FIXNUM) {AF07239}>
CL-USER> (defmethod foo :before ((a symbol) (b fixnum))
(format t "->"))
#<STANDARD-METHOD FOO :BEFORE (SYMBOL FIXNUM) {AF8D709}>
CL-USER> (foo 'test 5)
->TEST5
NIL
CL-USER> (defmethod foo :before ((a symbol) (b fixnum))
(format t "**"))
STYLE-WARNING: redefining FOO :BEFORE (SYMBOL FIXNUM) in DEFMETHOD
#<STANDARD-METHOD FOO :BEFORE (SYMBOL FIXNUM) {AFF4D61}>
CL-USER> (foo 'test 5)
**TEST5
NIL
Although this would facilitate changing the logging method once it's
been established, so I'm not sure if I want this or not.
And at present moment GP code is made up of functions, not methods. Some
of the functions use default parameter values. I would not be able to
easily transform them into methods (necessary to use :before and :after).
How would you implement it? Am I missing any obvious and elegant way?
Thank you for your help.
Benigno Ur�a.
On Apr 3, 6:02 pm, Benigno Uria <·············@gmail...> wrote:
> 2) Pass the logging functions as arguments to the GP code. I don't like
> this solution neither. The GP code already requires a long list of
> parameters. And the calls to the logging functions would obscure the GP
> code.
Or Pascal Costanza's dynamically scoped functions---so that they don't
occupy argument space.
AspectL, ContextL.
> I've made some experiments using the REPL and it seems I can only have
> only one :before and only one :after method for a given method.
For the same specialization yes. To get more, you have to vary the
specialization of the parameters, which can be inconvenient.
"Kaz Kylheku" <········@gmail.com> writes:
> On Apr 3, 6:02 pm, Benigno Uria <·············@gmail...> wrote:
>> 2) Pass the logging functions as arguments to the GP code. I don't like
>> this solution neither. The GP code already requires a long list of
>> parameters. And the calls to the logging functions would obscure the GP
>> code.
>
> Or Pascal Costanza's dynamically scoped functions---so that they don't
> occupy argument space.
>
> AspectL, ContextL.
>
>> I've made some experiments using the REPL and it seems I can only have
>> only one :before and only one :after method for a given method.
>
> For the same specialization yes. To get more, you have to vary the
> specialization of the parameters, which can be inconvenient.
But you don't need more than one :before or :after method. If you
want to log to several logs, you can use a broadcast-stream, or
implement a similar mechanism with your log system.
(defvar *log* (make-broadcast-stream)) ; /dev/null by default.
(defun log (ctrlstr &rest args) (apply (function format) *log* ctrlstr args))
(defmethod do-something :before (self) (log "~&Will do something~%"))
(defmethod do-something :after (self) (log "~&Did something~%"))
;; And now you can write:
(defclass x () ())
(defmethod do-something ((self x)) (format t "~&I'm doing something~%"))
[94]> (with-open-file (log "/tmp/log" :direction :output :if-does-not-exist :create :if-exists :append)
(let ((*log* (make-broadcast-stream log *standard-output*)))
(do-something (make-instance 'x))))
Will do something
I'm doing something
Did something
NIL
[95]> (shell "cat /tmp/log") ; I ran the above sexp several times already ;-)
Will do something
Did something
Will do something
Did something
Will do something
Did something
0
[96]>
--
__Pascal Bourguignon__
http://www.informatimago.com
http://pjb.ogamita.org
Kaz Kylheku wrote:
> On Apr 3, 6:02 pm, Benigno Uria <·············@gmail...> wrote:
>> 2) Pass the logging functions as arguments to the GP code. I don't like
>> this solution neither. The GP code already requires a long list of
>> parameters. And the calls to the logging functions would obscure the GP
>> code.
>
> Or Pascal Costanza's dynamically scoped functions---so that they don't
> occupy argument space.
>
> AspectL, ContextL.
>
>> I've made some experiments using the REPL and it seems I can only have
>> only one :before and only one :after method for a given method.
>
> For the same specialization yes. To get more, you have to vary the
> specialization of the parameters, which can be inconvenient.
>
I've skimmed AspectL homepage and although it looks useful I won't use
it this time. I don't want to force the GP lib users to understand AOP
and AspectL too. I want to keep the library as simple as possible.
Thank you.
Benigno Ur�a
On Apr 4, 2:02 am, Benigno Uria <·············@gmail...> wrote:
> I want to be able to use different loggers: log to screen, log to a
> database, log to files... Ideally I would be able to use many of these
> at the same time; but that's not really critical, just desirable.
>
>
> How would you implement it? Am I missing any obvious and elegant way?
Make all your logging use standard interfaces (functions). Have a
special variable called *LOGGER* say, and implement the user functions
as:
(defun log (...)
(log/logger *logger* ...))
LOG/LOGGER is, of course, a GF which can now specialize on its first
argument as well as any others. You want default methods for *logger*
being NULL, and probably a method like this:
(defmethod log/logger ((logger list) ...)
(loop for l in logger
do (log/logger l ...)))
Finally have a WITH-LOGGER macro.