Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eval in Common Lisp


I am new to lisp, and here is my question:
I have a list that is something like

((a ((length 3) (size 5))) (b ((length 5) (size 7))))...

The above list is just a sample.
What I am trying to have is a function find that can work like a database query, for example

(find (and (gt length 4) (lt size 8)))

I this case, the above function should find b for me. Note that the conditional argument of this function can be expand with and or or ...
I did some research and know eval can somehow help me with this, but I am not sure how it works exactly.
Can someone please show me an example or give me some hint on this?

Thank you

like image 614
Allan Jiang Avatar asked Jan 21 '26 11:01

Allan Jiang


2 Answers

I would not use eval for this. But it would be relatively easy to do so.

You have a sequence of items:

((a ((length 3) (size 5)))
 (b ((length 5) (size 7))))

You have a test description, like this:

(and (> length 4) (< size 8))

Now you want to see if

(my-equal '(and (> length 4) (< size 8)) '((length 5) (size 7))

is true.

So the task would be to write MY-EQUAL. Typically I would write it as a recursive function.

But if you want to do it with EVAL, it gets relatively easy:

You want to eval this form:

(let ((length 5) (size 7))
  (and (> length 4) (< size 8)))

Now it should be easy to write MY-EQUAL.

You can use it then as

(find term sequence :test #'my-equal :key #'second)

Note, that evaluation of arbitrary code read from a stream is a security risk.

Bonus

We can use COMPILE instead of EVAL:

(defun lookup (v bindings)
  (let ((result (assoc v bindings)))
    (if result
        (second result)
      (error "variable ~a not known" v))))

(defparameter *query-operators* '(and or > < =))

(defun generate-query-code (q bindings)
  (cond ((numberp q) q)
        ((symbolp q) `(lookup ',q ,bindings))
        ((consp q)
         (destructuring-bind (op . args)
             q
           (if (member op *query-operators*)
               `(,op ,@(mapcar (lambda (arg)
                                 (generate-query-code arg bindings))
                               args))
             (error "Unknown op ~a" op))))))

(defun compile-query (q)
  (compile nil
           (let* ((bindings (gensym "bindings"))
                  (code (generate-query-code q bindings)))
             `(lambda (,bindings)
                ,code))))

(defun find-query (query descriptions)
  (find-if (compile-query query)
           descriptions
           :key #'second))

Example:

CL-USER 39 > (find-query '(and (> length 4) (< size 8))
                         '((a ((length 3) (size 5)))
                           (b ((length 5) (size 7)))))
(B ((LENGTH 5) (SIZE 7)))
like image 85
Rainer Joswig Avatar answered Jan 23 '26 05:01

Rainer Joswig


This blog post looks related to your problem: http://xach.livejournal.com/131456.html

(you have to expand it with a small mapping between length and size and actual values for each of your records, so the closure chain can be called on each of your records)

like image 28
Miron Brezuleanu Avatar answered Jan 23 '26 04:01

Miron Brezuleanu