[newbie] evaluating a list as an expression

Discussion of Common Lisp
Post Reply
qiemem
Posts: 4
Joined: Sat Jan 17, 2009 8:00 pm

[newbie] evaluating a list as an expression

Post by qiemem » Sat Jan 17, 2009 8:23 pm

Hi all,

I'm trying to write a function that takes in a list (well, any expression actually), a list of symbols, and a list of values for those symbols, and then evaluates that list as an expression with those symbols assigned to those values. Perhaps my current attempt will explain this better than a description:

Code: Select all

(defun eval-expr (expr symbols assignments)
   (progv symbols assignments (eval expr)))
And then you could do something like:

Code: Select all

* (let ((expr '(and p q)))
    (eval-expr expr '(p q) '(t t)))

=> T
This works fairly well, but gives me warnings about undefined variables most of the time (not in the above instance). As I'm new to common lisp, I was wondering if there is a better way of doing this, one that wouldn't produce warnings.

The following gives me warnings (though still returns the correct value):

Code: Select all

* (let ((expr '(or p q)))
    (eval-expr expr '(p q) '(nil t)))

=> T
where the warnings are:

Code: Select all

; in: LAMBDA NIL
;     (LET ((#:G758 P))
;       (IF #:G758 #:G758 (OR Q)))
;
; caught WARNING:
;   undefined variable: P

;     (OR Q)
;
; caught WARNING:
;   undefined variable: Q

;
; caught WARNING:
;   These variables are undefined:
;     P Q
;
; compilation unit finished
;   caught 3 WARNING conditions
Thanks!

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: [newbie] evaluating a list as an expression

Post by ramarren » Sun Jan 18, 2009 2:17 am

First, why are you even trying to do that? It is generally recommended that you don't use EVAL, or for that matter PROGV, unless you know exactly what you are doing.

And what are you trying to achieve anyway? PROGV establishes dynamic bindings, and I can't tell if this is what you want... in any case it would almost certainly be better to transform the expression to establish bindings. For lexical bindings:

Code: Select all

(defun eval-expr (expr symbols assignments)
  (eval `(let ,(loop for symbol in symbols
                     for assignment in assignments
                     collect (list symbol assignment))
           ,expr)))

dmitry_vk
Posts: 96
Joined: Sat Jun 28, 2008 8:01 am
Location: Russia, Kazan
Contact:

Re: [newbie] evaluating a list as an expression

Post by dmitry_vk » Sun Jan 18, 2009 3:14 am

Maybe it would be better to compile a function and then call it with the arguments. Then it will be possible to reuse the function (in case the expression is needed to be evaluated with many arguments values):

Code: Select all

(defun make-function-definition (expr variables)
  `(lambda (,@variables) ,expr))

(defun make-function (expr variables)
  (compile nil (make-function-definition expr variables)))

(funcall (make-function '(* a b c) '(a b c)) 3 4 5)
=>
60

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: [newbie] evaluating a list as an expression

Post by nuntius » Sun Jan 18, 2009 11:37 am

First note: When dealing with things like compiler warnings, its usually good to mention the implementation you are using...

I asked about this on SBCL-help. The response was twofold.

This warning is not issued when SB-EXT:*EVALUATOR-MODE* is :INTERPRET.

When it is :COMPILE, "nontrivial" forms are compiled. The AND macro is trivial; so it was directly interpreted. The OR macro is implemented using LET, and LET is nontrivial. Thus SBCL evaluates (eval '(or p q)) by compiling (lambda () (or p q)) and then FUNCALLing the result. The warning is issued since P and Q were not declared as special variables when the form was compiled.

qiemem
Posts: 4
Joined: Sat Jan 17, 2009 8:00 pm

Re: [newbie] evaluating a list as an expression

Post by qiemem » Sun Jan 18, 2009 7:14 pm

Ramarren wrote:First, why are you even trying to do that?
I made some functions to generate random logical expressions and will write a program that generates programs which take in as input these expressions and output truth values for each of the variables. I need a function that tests the answers the generated programs give to see if they are correct.
Ramarren wrote:It is generally recommended that you don't use EVAL, or for that matter PROGV, unless you know exactly what you are doing.
I know EVAL should generally be avoided, but I wasn't sure how to do it without using EVAL; that's part of what caused me to ask.
I did not really understand that about PROGV, so thanks! I was just using it as a LET with a different input format.
dmitry_vk wrote:Maybe it would be better to compile a function and then call it with the arguments.
I was looking for something closer to this. However, in the ultimate program, the expressions sent to this function will contain only logical functions (AND, OR, and NOT), so compiling will probably do more harm than good in this case. Is there a way to get this to work without using COMPILE or EVAL? I tried messing with that code a bit, but I'm pretty new to Lisp and couldn't get it to work.
nuntius wrote:First note: When dealing with things like compiler warnings, its usually good to mention the implementation you are using...
Apologies. I'm using SBCL 1.0.23
nuntius wrote:When it is :COMPILE, "nontrivial" forms are compiled. The AND macro is trivial; so it was directly interpreted. The OR macro is implemented using LET, and LET is nontrivial. Thus SBCL evaluates (eval '(or p q)) by compiling (lambda () (or p q)) and then FUNCALLing the result. The warning is issued since P and Q were not declared as special variables when the form was compiled.
That certainly clears things up. I was very confused about why one worked and not the other. Thanks!

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: [newbie] evaluating a list as an expression

Post by ramarren » Mon Jan 19, 2009 1:11 am

qiemem wrote: However, in the ultimate program, the expressions sent to this function will contain only logical functions (AND, OR, and NOT), so compiling will probably do more harm than good in this case. Is there a way to get this to work without using COMPILE or EVAL?
For constricted languages like that an interpreter or minimal compiler is usually trivial. If you are not going to evaluate the same expression repeatedly, then an interpreter would be better. And a specialized one would likely be faster than one going through entire EVAL machinery. A simple example:

Code: Select all

;; if symbol table is very small then an alist might be better
(defun build-symbol-table (symbols assignments)
  (let ((symbol-table (make-hash-table)))
    (loop for symbol in symbols
          for assignment in assignments
          do (setf (gethash symbol symbol-table) assignment))
    symbol-table))

(defun eval-subexpression (expr symbol-table)
  (if (atom expr)
      (let ((symbol-bound
             (gethash expr symbol-table :missing)))
        (assert (not (eql symbol-bound :missing)))
        symbol-bound)
      (ecase (car expr)
        (or
           (loop for subexpr in (cdr expr)
                 thereis (eval-subexpression subexpr symbol-table)))
        (and
           (loop for subexpr in (cdr expr)
                 always (eval-subexpression subexpr symbol-table)))
        (not
           (assert (= (length expr) 2))
           (not (eval-subexpression (cadr expr) symbol-table))))))

(defun eval-expr (expr symbols assignments)
  (eval-subexpression expr (build-symbol-table symbols assignments)))

Post Reply