Page 1 of 1

[newbie] evaluating a list as an expression

PostPosted: Sat Jan 17, 2009 8:23 pm
by qiemem
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!

Re: [newbie] evaluating a list as an expression

PostPosted: Sun Jan 18, 2009 2:17 am
by ramarren
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)))

Re: [newbie] evaluating a list as an expression

PostPosted: Sun Jan 18, 2009 3:14 am
by dmitry_vk
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

Re: [newbie] evaluating a list as an expression

PostPosted: Sun Jan 18, 2009 11:37 am
by nuntius
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.

Re: [newbie] evaluating a list as an expression

PostPosted: Sun Jan 18, 2009 7:14 pm
by qiemem
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!

Re: [newbie] evaluating a list as an expression

PostPosted: Mon Jan 19, 2009 1:11 am
by ramarren
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)))