我是lisp的新手,这是我的问题:
我有一个类似
((a ((length 3) (size 5))) (b ((length 5) (size 7))))...
以上列表只是一个示例。
我想要的是一个函数find
,它可以像数据库查询一样工作,例如
(find (and (gt length 4) (lt size 8)))
在这种情况下,上面的函数应该为我找到b
。请注意,此函数的条件参数可以使用and
或or
进行扩展...
我做了一些研究,并知道eval
能以某种方式帮助我,但我不确定它是如何运作的。
有人可以给我一个例子,或者给我一些暗示?
谢谢
答案 0 :(得分:4)
我不会为此使用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)))
答案 1 :(得分:4)
此博客文章与您的问题相关:http://xach.livejournal.com/131456.html
(您必须使用length
和size
之间的小映射以及每条记录的实际值进行扩展,以便可以在每条记录上调用闭包链。