我不會爲此使用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
做到這一點,它會比較容易:
你想EVAL這種形式:
(let ((length 5) (size 7))
(and (> length 4) (< size 8)))
現在,應該是容易寫MY-相等。
您可以使用它那麼作爲
(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)))