我不会为此使用 eval 。但这样做相对容易。
您有一系列项目:
((a ((length 3) (size 5)))
(b ((length 5) (size 7))))
您有一个测试描述,如下所示:
(and (> length 4) (< size 8))
现在你想看看是否
(my-equal '(and (> length 4) (< size 8)) '((length 5) (size 7))
是真的。
所以任务是写MY-EQUAL
。通常我会把它写成一个递归函数。
但是如果你想用 来做EVAL
,它变得相对容易:
你想评估这个表格:
(let ((length 5) (size 7))
(and (> length 4) (< size 8)))
现在应该很容易编写 MY-EQUAL。
您可以将其用作
(find term sequence :test #'my-equal :key #'second)
请注意,从流中读取的任意代码的评估存在安全风险。
奖金
我们可以用COMPILE
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))
例子:
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)))