From: Brian Kendig
Subject: Am I using EVAL correctly here?
Date: 
Message-ID: <1992May19.195016.27013@Princeton.EDU>
The FAQ says that the eval function in Common Lisp should be avoided,
so I'm looking over a program I'm writing to figure out how to get by
without it, and I'm not sure how I can or if I should.

The part of the program I'm having trouble with is simple: given a
string, execute a particular function in response to that string.
This is for an AI "robot" who will say hello if you say hello, or shut
down is you tell him to quit, and so forth.

I have a list of strings/responses.  The one to match responding to a
hello is:

	'(("hello" "hi" "greetings")
	  (command 'say "Hi!"))

But if I want him to quit, I want to have the robot only quit if
someone says "<robot-name>, quit".  So I whipped this up:

	'(((concatenate 'string *robot-name* ", quit"))
	  (command 'quit))

Notice that the first element of the "hello" string list is a string,
whereas the first element of the "quit" string list is a list (that
evaluates to a string).  I use this in my check routine; when I want
to grab a string to match, I do:

	(let* ((temp (car string-list))  ; this grabs the first element
	       (string (if (listp temp) (eval temp) temp)))  ; got the string
	  ...	

This seems very clumsy to me.  Is there a simpler way of doing this, or
do I really need to use eval in this case?


Now it comes time to actually execute the comand I've got.  My check
routine will return a function call to execute if it finds a match to
a string I give it, or else nil if it can't find a match.  In other
words, for this example, (get-response "hello") returns (command 'say
"Hi!).  So I say:

	(let ((response (get-response string)))
	  (if response (eval response)))

Is _this_ a frowned-upon use of eval?

Any and all help would be appreciated!  Email me, and I'll summarize
here what I've got if I can get this figured out.  Thanks!

     << Brian >>

-- 
| Brian S. Kendig       --/\-- Tri     ········@phoenix.Princeton.EDU, @PUCC
| Computer Science BSE  |/  \| Quad  You gave your life to become the person
| Princeton University  /____\ clubs    you are right now.  Was it worth it?
From: lou
Subject: Re: Am I using EVAL correctly here?
Date: 
Message-ID: <LOU.92May19214638@atanasoff.rutgers.edu>
In article <······················@Princeton.EDU> ········@light.Princeton.EDU (Brian Kendig) writes:

   The part of the program I'm having trouble with is simple: given a
   string, execute a particular function in response to that string.
   [...]
   But if I want him to quit, I want to have the robot only quit if
   someone says "<robot-name>, quit".  So I whipped this up:

	   '(((concatenate 'string *robot-name* ", quit"))
	     (command 'quit))

Well, if you don't mind the variable *robot-name* being evaluated when
you build the list of commands, how about

	   `((,(concatenate 'string *robot-name* ", quit")
	      (command 'quit))
             ...)

If you want the variable evaluated at lookup time, do this:

	   `((,#'(lambda () (concatenate 'string *robot-name* ", quit"))
	      (command 'quit))
	     ...)

(I assume *robot-name* is a defvar.  It must be dynamically scoped, or
at least the place that creates the lookup table and the place that
sets it and the place that uses it must be in the scope of the SAME
binding of *robot-name*)

Then, to check an entry, do

   (let* ((temp (car string-list))  ; this grabs the first element
	  (string (if (stringp temp) temp (funcall temp))))
	     ...)

This uses funcall, not eval, which for a number of reasons is much better.


   Now it comes time to actually execute the comand I've got.  ... In other
   words, for this example, (get-response "hello") returns (command 'say
   "Hi!).  So I say:

	   (let ((response (get-response string)))
	     (if response (eval response)))

   Is _this_ a frowned-upon use of eval?

Yep.  Try this instead:  Make your table of command action look like
	`(("hello"
	    ,#'(lambda () (command 'say "Hi!")))
	  ...)
I.e. the command is now a funcallable object, not a list to eval.  Then

	   (let ((response (get-response string)))
	     (if response (funcall response)))

The general principle is to create functions with #' instead of creating
lists, so you can use funcall instead of eval.
--
					Lou Steinberg

uucp:   {pretty much any major site}!rutgers!aramis.rutgers.edu!lou 
internet:   ···@cs.rutgers.edu