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
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)))
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With