From: Bruce J. Weimer, MD
Subject: Programs redefining functions
Date: 
Message-ID: <vj3jlao7fqkg15@corp.supernews.com>
This is a newbie question:

I've programmed a whole bunch of "actions" that then have "consequences" -
specifically, taking an action changes the values of 8 global / special
"emotions".  An example looks like this:

(defun WALK ()
   (setq *HAPPY* (+ *HAPPY* 0))
   (setq *TIRED* (+ *TIRED* 1.1))
   (setq *HUNGRY* (+ *HUNGRY* 1))
   (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
   (setq *BORED* (+ *BORED* 1))
   (setq *LONELY* (+ *LONELY* 1))
   (setq *CROWDED* (/ *CROWDED* 1.25))
   (setq *SCARED* (/ *SCARED* 1.1)))

The whole program compiles, loads and runs just fine - when the program
"walks", it gets a little more tired, a little more hungry, a little less
scared, etc.  (It's an AI simulation - and it's already exhibiting some
interesting "behaviors.")

The problem is this - suppose that after some "experience" the program
"decides" that the definition should really have been:

(defun WALK ()
   (setq *HAPPY* (/ *HAPPY* 1.2))
   (setq *TIRED* (+ *TIRED* 1.3))
   (setq *HUNGRY* (+ *HUNGRY* 1.1))
   (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
   (setq *BORED* (/ *BORED* 1.1))
   (setq *LONELY* (+ *LONELY* 1))
   (setq *CROWDED* (/ *CROWDED* 1.25))
   (setq *SCARED* (/ *SCARED* 1.25)))

How can the program redefine a function that's already been compiled?  I
mean that I want the program to do this automatically as it's running - I
don't want to manually stop, edit, save, recompile, etc.  What's the best
way to do this?  Any suggestions?

Thanks in advance.

Bruce.

From: Kenny Tilton
Subject: Re: Programs redefining functions
Date: 
Message-ID: <3F31DB4D.1000905@nyc.rr.com>
Bruce J. Weimer, MD wrote:
> This is a newbie question:
> 
> I've programmed a whole bunch of "actions" that then have "consequences" -
> specifically, taking an action changes the values of 8 global / special
> "emotions".  An example looks like this:
> 
> (defun WALK ()
>    (setq *HAPPY* (+ *HAPPY* 0))
>    (setq *TIRED* (+ *TIRED* 1.1))
>    (setq *HUNGRY* (+ *HUNGRY* 1))
>    (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
>    (setq *BORED* (+ *BORED* 1))
>    (setq *LONELY* (+ *LONELY* 1))
>    (setq *CROWDED* (/ *CROWDED* 1.25))
>    (setq *SCARED* (/ *SCARED* 1.1)))
> 
> The whole program compiles, loads and runs just fine - when the program
> "walks", it gets a little more tired, a little more hungry, a little less
> scared, etc.  (It's an AI simulation - and it's already exhibiting some
> interesting "behaviors.")
> 
> The problem is this - suppose that after some "experience" the program
> "decides" that the definition should really have been:
> 
> (defun WALK ()
>    (setq *HAPPY* (/ *HAPPY* 1.2))
>    (setq *TIRED* (+ *TIRED* 1.3))
>    (setq *HUNGRY* (+ *HUNGRY* 1.1))
>    (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
>    (setq *BORED* (/ *BORED* 1.1))
>    (setq *LONELY* (+ *LONELY* 1))
>    (setq *CROWDED* (/ *CROWDED* 1.25))
>    (setq *SCARED* (/ *SCARED* 1.25)))
> 
> How can the program redefine a function that's already been compiled?  I
> mean that I want the program to do this automatically as it's running - I
> don't want to manually stop, edit, save, recompile, etc.  What's the best
> way to do this?  Any suggestions?

Two.

First, don't do it this way. :) Let data be data. It will also let you 
have more than one person, which you will want when *romantic* gets to a 
certain threshold.

(defstruct (person (:conc-name nil) happy etc)

  (defun walk (person)
    (incf (happy person) 1.2) ;; same as (setf a (+ a 1.2))
    ..etc..)

Second (pardon any line wraps):


(defun redefun (function-name &rest new-slots)
   (let ((new-func `(defun ,function-name ()
                      ,@(mapcar (lambda (new-setq)
                                  `(setq ,(car new-setq)
                                     ,(cadr new-setq)))
                          new-slots))))
     (eval new-func)
     (compile function-name)
     new-func))

(redefun 'walk (list '*happy* (list '/ '*happy* 1.2)))

or (redefun 'walk `(*happy* (/ *happy* ,new-happy)))

(defparameter *happy* 1.44)

(walk)
=> 1.2
(walk)
=> 1.0

hth.

-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Career highlights? I had two. I got an intentional walk from
Sandy Koufax and I got out of a rundown against the Mets."
                                                  -- Bob Uecker
From: Peter Seibel
Subject: Re: Programs redefining functions
Date: 
Message-ID: <m3y8y6rqjw.fsf@javamonkey.com>
"Bruce J. Weimer, MD" <······@mminternet.com> writes:

> This is a newbie question:
> 
> I've programmed a whole bunch of "actions" that then have "consequences" -
> specifically, taking an action changes the values of 8 global / special
> "emotions".  An example looks like this:
> 
> (defun WALK ()
>    (setq *HAPPY* (+ *HAPPY* 0))
>    (setq *TIRED* (+ *TIRED* 1.1))
>    (setq *HUNGRY* (+ *HUNGRY* 1))
>    (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
>    (setq *BORED* (+ *BORED* 1))
>    (setq *LONELY* (+ *LONELY* 1))
>    (setq *CROWDED* (/ *CROWDED* 1.25))
>    (setq *SCARED* (/ *SCARED* 1.1)))
> 
> The whole program compiles, loads and runs just fine - when the program
> "walks", it gets a little more tired, a little more hungry, a little less
> scared, etc.  (It's an AI simulation - and it's already exhibiting some
> interesting "behaviors.")
> 
> The problem is this - suppose that after some "experience" the program
> "decides" that the definition should really have been:
> 
> (defun WALK ()
>    (setq *HAPPY* (/ *HAPPY* 1.2))
>    (setq *TIRED* (+ *TIRED* 1.3))
>    (setq *HUNGRY* (+ *HUNGRY* 1.1))
>    (setq *ROMANTIC* (/ *ROMANTIC* 1.2))
>    (setq *BORED* (/ *BORED* 1.1))
>    (setq *LONELY* (+ *LONELY* 1))
>    (setq *CROWDED* (/ *CROWDED* 1.25))
>    (setq *SCARED* (/ *SCARED* 1.25)))
> 
> How can the program redefine a function that's already been compiled?  I
> mean that I want the program to do this automatically as it's running - I
> don't want to manually stop, edit, save, recompile, etc.  What's the best
> way to do this?  Any suggestions?

One way, is simply to avoid having to redefine the function. Make the
parts that can change into data. Namely function objects. Since the
basic form of your WALK function is to adjust a bunch of values
according to some function that's different for each variable, you
could define another set of variables that will hold the current
adjustment function for that variable and rewrite write WALK like
this:

  (defun WALK ()
     (setq *HAPPY*    (funcall *adjust-happiness* *HAPPY*))
     (setq *TIRED*    (funcall *adjust-tiredness* *TIRED*))
     (setq *HUNGRY*   (funcall *adjust-hungriness* *HUNGRY*))
     (setq *ROMANTIC* (funcall *adjust-romanticness* *ROMANTIC*))
     (setq *BORED*    (funcall *adjust-boredness* *BORED*))
     (setq *LONELY*   (funcall *adjust-lonlieness* *LONELY*))
     (setq *CROWDED*  (funcall *adjust-crowededness* *CROWDED*))
     (setq *SCARED*   (funcall *adjust-scaredness* *SCARED*)))

And then set the *adjust-xxx* variables to functions that take one
value and return another. If they're all of the form you showed above,
you could make a function to generate these functions such as:

  (defun make-adjuster (op adjustment)
    #'(lambda (old-value) (funcall op old-value adjustment)))

Now you can define your adjustment functions:

  (defvar *adjust-happiness* (make-adjuster #'+ 0))

etc. And then when you want to change the behavior of WALK you can
simply change the appropriate adjusters. E.g.:

  (setq *adjust-happiness* (make-adjuster #'/ 1.2))

Of course if you have functions other than WALK that also adjust these
variables, you'll need a separte set of adjuster functions for each
peer of WALK. At that point you might not want to store each one in
its own variable--since how each variable is adjusted is presumably a
function of both the high level function (e.g. WALK) and what variable
it is, it'll probably be more straightforward to store the adjuster
functions in a data structure that does this double mapping for you.
For instance you could use a an EQUAL hashtable key'd by a list of
main function and the variable:

  (defvar *adjusters* (make-hash-table :test #'equal))

  (setf (gethash (list 'WALK '*HAPPY*') *adjusters* (make-adjuster #'+ 0)))

Then you can write a single generic adjustment function:

  (defun adjust-variable-for-action (action variable old-value)
    (funcall (gethash (list action variable) *adjusters*) old-value))

and WALK looks more like:

  (defun WALK ()
     (setq *HAPPY*    (adjust-variable-for-action 'WALK '*HAPPY* *HAPPY*))
     (setq *TIRED*    (adjust-variable-for-action 'WALK '*TIRED* *TIRED*))
     (setq *HUNGRY*   (adjust-variable-for-action 'WALK '*HUNGRY* *HUNGRY*))
     (setq *ROMANTIC* (adjust-variable-for-action 'WALK '*ROMANTIC* *ROMANTIC*))
     (setq *BORED*    (adjust-variable-for-action 'WALK '*BORED* *BORED*))
     (setq *LONELY*   (adjust-variable-for-action 'WALK '*LONELY* *LONELY*))
     (setq *CROWDED*  (adjust-variable-for-action 'WALK '*CROWDED* *CROWDED*))
     (setq *SCARED*   (adjust-variable-for-action 'WALK '*SCARED*) *SCARED*)))

Of course all that duplication of the variable names (and the use of
the variable name both quoted and unquoted) cries out for a macro:

  (defmacro adjust-variable-for-action (action variable)
    `(setq ,variable
       (funcall (gethash (list ',action ',variable) *adjusters*) ,variable)))
       
  (defun WALK ()
     (adjust-variable-for-action WALK *HAPPY*)
     (adjust-variable-for-action WALK *TIRED*)
     (adjust-variable-for-action WALK *HUNGRY*)
     (adjust-variable-for-action WALK *ROMANTIC*)
     (adjust-variable-for-action WALK *BORED*)
     (adjust-variable-for-action WALK *LONELY*)
     (adjust-variable-for-action WALK *CROWDED*)
     (adjust-variable-for-action WALK *SCARED*))

Which still seems pretty verbose. Another macro can condense things:

  (defmacro adjust-variables-for-action (action &rest variables)
    `(progn
       ,@(mapcar #'(lambda (v) `(adjust-variable-for-action ,action ,v)) variables)))

  (defun WALK ()
    (adjust-variable-for-action 
     WALK
     *HAPPY* *TIRED* *HUNGRY* *ROMANTIC*
     *BORED* *LONELY* *CROWDED* *SCARED*))

Or if all the action functions adjust all the same variables, you can
hardwire them into the definition of adjust-variables-for-action at
which point WALK becomes:

  (defun WALK () (adjust-variables-for-action WALK))

Finally, you'll probably want to provide a nice interface to changing
the adjustment functions. Something like this is nicely hides the
hash-table allowing you to change it some other datastructure later:

  (defun change-adjustment-function (action variable adjuster)
    (setf (gethash (list action variable) *adjusters*) adjuster))

All of this can no doubt be improved in many ways. (Moving the emotion
variables into some sort of composite data type such as a class,
struct, or even a humble alist or hashtable leaps to mind. You'll
certainly need to do that if you ever want to have more than one of
these critters.)

But it does seem to me that while Lisp's ability to redefine functions
on the fly is more of a development and debugging convenience. If
there is something about your program that you *know* is going to
change at run time, that should be treated as data. Luckily Lisp also
lets us treat functions as data (which is orthogonal to the ability to
redefine named functions) which is what my sketch here uses.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Marco Baringer
Subject: Re: Programs redefining functions
Date: 
Message-ID: <m2adamf2z9.fsf@bese.it>
"Bruce J. Weimer, MD" <······@mminternet.com> writes:

> How can the program redefine a function that's already been compiled?  I
> mean that I want the program to do this automatically as it's running - I
> don't want to manually stop, edit, save, recompile, etc.  What's the best
> way to do this?  Any suggestions?

i'm going to assume you don't actually want to create the function
body at runtime.

we'll want to allow for multiple people to coexist so each person
will be represented as a ste of values for happy, tired, hungry,
etc. we'll also allow for each person to be affected by these values
in different ways. so:

(defstruct (person (:conc-name person.))
  happy happy-update
  tired tired-update
  hungry hungry-update
  romantic romantic-update
  bored bored-update
  lonely lonely-update
  crowded crowded-update
  scared scared-update)

(defun update-person (person)
  (setf (person.happy person) (funcall (person.happy-update person) person)
        (person.tired person) (funcall (person.tired-update person) person)
        ...))

now when we create a person we give them the "standard" update
functions:

(make-person :happy 1 
             :happy-update (lambda (person) (person.happy person))
             :tired 1 
             :tired-update (lambda (person) (+ 1.1 (person.tired person)))
             ....)

then when we realise that happy needs to be updated differently we
simply do:

(setf (person.happy-update person)
      (lambda (person) (* 2 (person.happy person))))

get the idea? if you're sure that everbody will always have the same
update functions you can just as well put them it global vars:

(defvar *happy-update* (lambda (person) (1+ (person.happy person))))

...

(defun update-person (person)
  (setf (person.happy person) (funcall *happy-update* person)))

either way i'd suggest you create a few macros to make it all a bit
simpler to write.

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen
From: Alexey Dejneka
Subject: Re: Programs redefining functions
Date: 
Message-ID: <m3ptjiul6f.fsf@comail.ru>
"Bruce J. Weimer, MD" <······@mminternet.com> writes:

> (defun WALK ()
[...]
> The whole program compiles, loads and runs just fine - when the
> program "walks", it gets a little more tired, a little more hungry,
> a little less scared, etc. (It's an AI simulation - and it's already
> exhibiting some interesting "behaviors.")
> 
> The problem is this - suppose that after some "experience" the
> program "decides" that the definition should really have been:
[...]
> How can the program redefine a function that's already been
> compiled? I mean that I want the program to do this automatically as
> it's running - I don't want to manually stop, edit, save, recompile,
> etc. What's the best way to do this? Any suggestions?

(declaim (notinline walk)) in the beginning of your program and

(setf (fdefinition 'walk)
      (coerce `(lambda () ,@(make-new-walk-body)) 'function))

or

(compile 'walk `(lambda () ,@(make-new-walk-body)))

-- 
Regards,
Alexey Dejneka

"Alas, the spheres of truth are less transparent than those of
illusion." -- L.E.J. Brouwer
From: lin8080
Subject: Re: Programs redefining functions
Date: 
Message-ID: <3F357567.AB3E215E@freenet.de>
"Bruce J. Weimer, MD" schrieb:

> The problem is this - suppose that after some "experience" the program
> "decides" that the definition should really have been:
...

Hey

When you want this inside a game, the suggestions above seemed very
large and slow. Why not keep things simple and use a normal list?

Your (setq *TIRED* (+ *TIRED* 1.1)) may then look like: (setf *tired* (+
(nth 2 'person3) 1.1)). (assuming that the values belongs to person3)

You can also see this as a vector-array (more access functionality) and
use something like (aref 'person3 2) to get (change) the value. (see
also if svref is available, its a bit quicker) 

Last you get a construct that may be call chromosome and means some
numbers who design the moments of person3. (compiling simple things will
not change the runtime-speed very much)

(the code should fit into another lisp-dialekt which is able to be
include inside another programm or do you want a cl-lisp solution?). 

Just my thoughts 

stefan
From: Matthew Danish
Subject: Re: Programs redefining functions
Date: 
Message-ID: <20030810195102.GO17568@lain.mapcar.org>
On Sun, Aug 10, 2003 at 12:27:51AM +0200, lin8080 wrote:
> Your (setq *TIRED* (+ *TIRED* 1.1)) may then look like: (setf *tired* (+
> (nth 2 'person3) 1.1)). (assuming that the values belongs to person3)

There's a large difference between (nth 2 'person3) and (nth 2 person3)
One of those two doesn't work because it tries to access the second
value of a symbol (rather than a list).

Same goes for AREF, with arrays.  It's a matter of using variables
correctly.

-- 
; Matthew Danish <·······@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."
From: lin8080
Subject: Re: Programs redefining functions
Date: 
Message-ID: <3F381774.234BED31@freenet.de>
Matthew Danish schrieb:
> 
> On Sun, Aug 10, 2003 at 12:27:51AM +0200, lin8080 wrote:
> > Your (setq *TIRED* (+ *TIRED* 1.1)) may then look like: (setf *tired* (+
> > (nth 2 'person3) 1.1)). (assuming that the values belongs to person3)
> 
> There's a large difference between (nth 2 'person3) and (nth 2 person3)
> One of those two doesn't work because it tries to access the second
> value of a symbol (rather than a list).
> 
> Same goes for AREF, with arrays.  It's a matter of using variables
> correctly.

right ok.

stefan
From: Bruce J. Weimer, MD
Subject: Re: Programs redefining functions
Date: 
Message-ID: <vjd043e5dn7m0e@corp.supernews.com>
Thank you all for your responses, comments and examples - I learn as much
from the suggestions that I don't end up using as from the one that I do!

Bruce.