Macros 编写宏的宏-编译错误

Macros 编写宏的宏-编译错误,macros,common-lisp,backquote,let-over-lambda,Macros,Common Lisp,Backquote,Let Over Lambda,当我编译下面的代码时,SBCL抱怨g-单位价值和g-单位未定义。我不知道如何调试这个。据我所知,扁平化正在失败 当“展平”到达defunits的未引用部分时,整个部分似乎被视为一个原子。听起来对吗 下面使用了本书中的代码: 保罗·格雷厄姆公用事业公司 (defun symb (&rest args) (values (intern (apply #'mkstr args)))) (defun mkstr (&rest args) (with-output-to-stri

当我编译下面的代码时,SBCL抱怨g-单位价值和g-单位未定义。我不知道如何调试这个。据我所知,扁平化正在失败

当“展平”到达defunits的未引用部分时,整个部分似乎被视为一个原子。听起来对吗

下面使用了本书中的代码:

保罗·格雷厄姆公用事业公司

(defun symb (&rest args)
  (values (intern (apply #'mkstr args))))

(defun mkstr (&rest args)
  (with-output-to-string (s)
    (dolist (a args) (princ a s))))

(defun group (source n)
  (if (zerop n) (error "zero length"))
  (labels ((rec (source acc)
             (let ((rest (nthcdr n source)))
               (if (consp rest)
                   (rec rest (cons (subseq source 0 n) acc))
                   (nreverse (cons source acc))))))
    (if source (rec source nil) nil)))

(defun flatten (x)
  (labels ((rec (x acc)
             (cond ((null x) acc)
                   ((atom x) (cons x acc))
                   (t (rec (car x) (rec (cdr x) acc))))))
    (rec x nil)))
让出Lambda公用设施-第3章

(defmacro defmacro/g! (name args &rest body)
  (let ((g!-symbols (remove-duplicates
               (remove-if-not #'g!-symbol-p
                              (flatten body)))))
    `(defmacro ,name ,args
       (let ,(mapcar
              (lambda (g!-symbol)
                `(,g!-symbol (gensym ,(subseq
                                       (symbol-name g!-symbol)
                                       2))))
              g!-symbols)
         ,@body))))

(defun g!-symbol-p (symbol-to-test)
  (and (symbolp symbol-to-test)
       (> (length (symbol-name symbol-to-test)) 2)
       (string= (symbol-name symbol-to-test)
                "G!"
                :start1 0
                :end1 2)))

(defmacro defmacro! (name args &rest body)
  (let* ((o!-symbols (remove-if-not #'o!-symbol-p args))
         (g!-symbols (mapcar #'o!-symbol-to-g!-symbol o!-symbols)))
    `(defmacro/g! ,name ,args
       `(let ,(mapcar #'list (list ,@g!-symbols) (list ,@o!-symbols))
          ,(progn ,@body)))))

(defun o!-symbol-p (symbol-to-test)
  (and (symbolp symbol-to-test)
       (> (length (symbol-name symbol-to-test)) 2)
       (string= (symbol-name symbol-to-test)
                "O!"
                :start1 0
                :end1 2)))

(defun o!-symbol-to-g!-symbol (o!-symbol)
  (symb "G!" (subseq (symbol-name o!-symbol) 2)))
放过Lambda-第5章

(defun defunits-chaining (u units prev)
  (if (member u prev)
      (error "~{ ~a~^ depends on~}"
             (cons u prev)))
  (let ((spec (find u units :key #'car)))
    (if (null spec)
        (error "Unknown unit ~a" u)
        (let ((chain (second spec)))
          (if (listp chain)
              (* (car chain)
                 (defunits-chaining
                     (second chain)
                     units
                   (cons u prev)))
              chain)))))

(defmacro! defunits (quantity base-unit &rest units)
  `(defmacro ,(symb 'unit-of- quantity)
       (,g!-unit-value ,g!-unit)
     `(* ,,g!-unit-value
         ,(case ,g!-unit
                ((,base-unit) 1)
                ,@(mapcar (lambda (x)
                            `((,(car x))
                              ,(defunits-chaining
                                (car x)
                                (cons
                                 `(,base-unit 1)
                                 (group units 2))
                                nil)))
                          (group units 2))))))

这有点棘手:

问题:假设反引号/逗号表达式是普通列表。

你需要问自己这个问题:

反引号/逗号表达式的表示形式是什么?

这是一张单子吗

实际上,完整的表示是未指定的。请看这里:

我们正在使用SBCL。见此:

* (setf *print-pretty* nil)

NIL


* '`(a ,b)

(SB-INT:QUASIQUOTE (A #S(SB-IMPL::COMMA :EXPR B :KIND 0)))
因此,逗号表达式由类型为
SB-IMPL::comma
的结构表示。SBCL开发人员认为,当需要使用漂亮的打印机打印此类反向报价列表时,这种表示方式会有所帮助

因为你的
展平
将结构视为原子,所以它不会查看内部

但这是SBCL的具体表现。Clozure CL做了其他事情,LispWorks又做了其他事情

Clozure CL:

? '`(a ,b)
(LIST* 'A (LIST B))
LispWorks:

CL-USER 87 > '`(a ,b)
(SYSTEM::BQ-LIST (QUOTE A) B)
调试

由于您发现其中涉及了
flatte
,接下来的调试步骤是:

首先:跟踪函数
flatten
,查看调用该函数的数据以及返回的数据

由于我们不确定数据实际上是什么,因此可以检查它

使用SBCL的调试示例:

* (defun flatten (x)                                                                                         
    (inspect x)                                                                                              
    (labels ((rec (x acc)                                                                                    
               (cond ((null x) acc)                                                                          
                     ((atom x) (cons x acc))                                                                 
                     (t (rec (car x) (rec (cdr x) acc))))))                                                  
      (rec x nil)))
STYLE-WARNING: redefining COMMON-LISP-USER::FLATTEN in DEFUN

FLATTEN
以上调用
检查
参数数据。在公共Lisp中,检查器通常是可以交互检查数据结构的东西

例如,我们使用反引号表达式调用
flant

* (flatten '`(a ,b))

The object is a proper list of length 2.
0. 0: SB-INT:QUASIQUOTE
1. 1: (A ,B)
我们在交互式检查器中。现在可用的命令包括:

> help

help for INSPECT:
  Q, E        -  Quit the inspector.
  <integer>   -  Inspect the numbered slot.
  R           -  Redisplay current inspected object.
  U           -  Move upward/backward to previous inspected object.
  ?, H, Help  -  Show this help.
  <other>     -  Evaluate the input as an expression.
Within the inspector, the special variable SB-EXT:*INSPECTED* is bound
to the current inspected object, so that it can be referred to in
evaluated expressions.
进一步走进:

> 1

The object is a STRUCTURE-OBJECT of type SB-IMPL::COMMA.
0. EXPR: B
1. KIND: 0
在这里,检查器告诉我们对象是某种类型的结构。这就是我们想知道的

现在,我们使用命令
q
离开检查器,
flatte
函数继续并返回一个值:

> q

(SB-INT:QUASIQUOTE A ,B)

对于任何其他想要得到它的人!要处理SBCL,此问题的临时解决方案是在展开过程中摸索unquote结构内部,递归展开其内容:

(defun flatten (x)
  (labels ((flatten-recursively (x flattening-list)
             (cond ((null x) flattening-list)
                   ((eq (type-of x) 'SB-IMPL::COMMA) (flatten-recursively (sb-impl::comma-expr x) flattening-list))
                   ((atom x) (cons x flattening-list))
                   (t (flatten-recursively (car x) (flatten-recursively (cdr x) flattening-list))))))
    (flatten-recursively x nil)))

但这严重依赖于平台。如果我找到更好的方法,我会把它贴出来。

如果还有人对这一个感兴趣,这是我的三分钱。我反对上述对
flatte
的修改,因为它可能会像最初一样自然地更有用,而unquote表示的问题在
defmacro/g中相当常见。我对
defmacro/g做了一个不太漂亮的修改使用功能来决定要做什么。也就是说,在处理非SBCL实现(
#-SBCL
)时,我们像以前一样进行处理,而在SBCL(
#+SBCL
)的情况下,我们深入研究了
sb impl::comma
结构,必要时使用其
expr
属性,并在
中使用
equalp
删除重复项,因为我们现在处理的是结构,不是符号。代码如下:

(defmacro defmacro/g! (name args &rest body)
  (let ((syms (remove-duplicates
               (remove-if-not #-sbcl #'g!-symbol-p
                              #+sbcl #'(lambda (s)
                                         (and (sb-impl::comma-p s)
                                              (g!-symbol-p (sb-impl::comma-expr s))))
                              (flatten body))
               :test #-sbcl #'eql #+sbcl #'equalp)))
    `(defmacro ,name ,args
       (let ,(mapcar
              (lambda (s)
                `(#-sbcl ,s #+sbcl ,(sb-impl::comma-expr s)
                         (gensym ,(subseq
                                   #-sbcl
                                   (symbol-name s)
                                   #+sbcl
                                   (symbol-name (sb-impl::comma-expr s))
                                   2))))
              syms)
         ,@body))))

它与SBCL一起工作。我还没有在其他实现上彻底测试它。

谢谢。哇,这是一个结构。我想没有人制作过可移植的Quasikote库,是吗?这正是我想要了解为什么defmacro/g!LOL()第3章中定义的宏不起作用!出于某种原因感谢函数g-symbol-p不在Clozure Common Lisp 1.11版(达尔文x8664)中编译!这与SBCL的做法类似吗?非常感谢您的修复。在尝试玩《放过兰姆达》中的一些例子时,他非常困惑。
(defmacro defmacro/g! (name args &rest body)
  (let ((syms (remove-duplicates
               (remove-if-not #-sbcl #'g!-symbol-p
                              #+sbcl #'(lambda (s)
                                         (and (sb-impl::comma-p s)
                                              (g!-symbol-p (sb-impl::comma-expr s))))
                              (flatten body))
               :test #-sbcl #'eql #+sbcl #'equalp)))
    `(defmacro ,name ,args
       (let ,(mapcar
              (lambda (s)
                `(#-sbcl ,s #+sbcl ,(sb-impl::comma-expr s)
                         (gensym ,(subseq
                                   #-sbcl
                                   (symbol-name s)
                                   #+sbcl
                                   (symbol-name (sb-impl::comma-expr s))
                                   2))))
              syms)
         ,@body))))