我不明白下面 condlet-clause 中第一个 LET 的用途。
`(,(car cl) (let ,(mapcar #'cdr vars)
由于这里没有定义具体值,所以这是必要的吗? 它只是声明局部变量。为什么要费力这样做呢?
(defmacro condlet (clauses &body body)
(let ((bodfn (gensym))
(vars (mapcar #'(lambda (v) (cons v (gensym)))
(remove-duplicates
(mapcar #'car
(mappend #'cdr clauses))))))
`(labels ((,bodfn ,(mapcar #'car vars)
,@body))
(cond ,@(mapcar #'(lambda (cl)
(condlet-clause vars cl bodfn))
clauses)))))
(defun condlet-clause (vars cl bodfn)
`(,(car cl) (let ,(mapcar #'cdr vars)
(let ,(condlet-binds vars cl)
(,bodfn ,@(mapcar #'cdr vars))))))
(defun condlet-binds (vars cl)
(mapcar #'(lambda (bindform)
(if (consp bindform)
(cons (cdr (assoc (car bindform) vars))
(cdr bindform))))
(cdr cl)))
最佳答案
基于this implementation of CONDLET
, condlet
可以像这样使用:
(condlet (((= 1 2) (x 1) (y 2))
((= 1 1) (x 2) (y 1))
(t (x 3) (z 3)))
(list x y z))
请注意,主体部分中出现了三个变量,x
, y
,和z
,但每个子句仅绑定(bind)两个:第一个和第二个绑定(bind) x
和y
,第三个绑定(bind) x
和z
。通过这样做
(let (x y z)
(let <bindings from actual clause>
(bodyfn x y z)))
宏保证 x
, y
,和z
全部都有默认值 nil
。 <bindings from actual clause>
将在词法上隐藏实际子句负责绑定(bind)的变量。不过,这有点简化。要了解实际发生的情况,让我们看看该示例的宏展开:
(pprint (macroexpand-1 '(condlet (((= 1 2) (x 1) (y 2))
((= 1 1) (x 2) (y 1))
(t (x 3) (z 3)))
(list x y z))))
;=>
(LABELS ((#:G973 (Y X Z) ; g973 = bodfn
(LIST X Y Z)))
(COND
((= 1 2)
(LET (#:G974 #:G975 #:G976) ; y(g974) = nil, x(g975) = nil, z(g976) = nil
(LET ((#:G975 1) (#:G974 2)) ; x = 1, y = 2
(#:G973 #:G974 #:G975 #:G976)))) ; (bodfn y x z)
((= 1 1)
(LET (#:G974 #:G975 #:G976) ; y = nil, x = nil, z = nil
(LET ((#:G975 2) (#:G974 1)) ; x = 2, y = 1
(#:G973 #:G974 #:G975 #:G976)))) ; (bodfn y x z)
(T
(LET (#:G974 #:G975 #:G976) ; y = nil, x = nil, z = nil
(LET ((#:G975 3) (#:G976 3)) ; x = 3, z = 4
(#:G973 #:G974 #:G975 #:G976)))))) ; (bodfn y x z)
关于macros - OnLisp 中的 CONDLET 宏对我来说并不简单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20510148/