在OnLisp的CONDLET宏中,对我来说并不简单

时间:2013-12-11 03:47:56

标签: macros lisp common-lisp paul-graham

我不明白下面 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))) 

1 个答案:

答案 0 :(得分:2)

基于this implementation of CONDLETcondlet可以像这样使用:

(condlet (((= 1 2) (x 1) (y 2))
          ((= 1 1) (x 2) (y 1))
          (t (x 3) (z 3)))
  (list x y z))

请注意,三个变量出现在正文部分xyz中,但每个条款仅绑定两个:第一个和第二个绑定xy,第三个绑定xz。通过做

(let (x y z)
  (let <bindings from actual clause>
    (bodyfn x y z)))

宏保证xyz的默认值均为nil<bindings from actual clause>将在词法上隐藏实际子句负责绑定的变量。不过,这有点简化。为了看看实际发生了什么,让我们来看看这个例子的宏观扩展:

(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)