Common Lisp Lisp-1 宏

Common Lisp Lisp-1 macro

我正在尝试在普通 lisp 中模拟 scheme 的单个命名空间,使用扩展为 lambda 的宏(基于 Doug Hoyte 的),其中每次使用 f! 符号(类似于 Doug Hoyte 的o!g! 符号)在函数位置扩展为相同的表达式,但在每次调用的函数位置添加 funcall。例如:

(fplambda (f!z x) (f!z x x))

将扩展为:

(LAMBDA (F!Z X) (FUNCALL F!Z X X))

宏目前看起来像这样:

(defmacro fplambda (parms &body body)
  (let ((syms (remove-duplicates
                (remove-if-not #'f!-symbol-p
                               (flatten body)))))
    `(lambda ,parms
       (macrolet ,(mapcar
               (lambda (f)
                 `(,f (&rest parmlist)  `(funcall ,',f ',@parmlist)))
               syms))
         ,@body)))

但鉴于上述输入,它扩展(据我所知)为:

(LAMBDA (F!F X)
       (MACROLET ((F!F (&REST PARMLIST) `(FUNCALL ,'F!F ',@PARMLIST))))
       (F!F X X))

在 macrolet 定义中,F!F 不应该被引用或不被引用,而 parmlist 应该被不被引用。到底是怎么回事? 提前致谢!

您的定义基本正确。你刚刚犯了两个非常简单的错误。第一个是不匹配的父母。 macrolet 不包含正文(在输出中,macrolet 和正文处于同一缩进级别)。

至于嵌套的反引号,唯一的错误是parmlist之前的引号。除此之外,其他一切都是正确的。 F!F 之前的逗号和引号实际上是正确的。来自 hyperspec: "An implementation is free to interpret a backquoted form F1 as any form F2 that, when evaluated, will produce a result that is the same under equal as the result implied by the above definition"。由于内部反引号尚未扩展,因此它不必没有引号和反引号。表达式 `(,'x) 实际上等同于 `(x).

众所周知,嵌套反引号非常复杂。理解它们的最简单方法可能是阅读 Steele's explanation of them.

编辑:

关于是否可以在函数位置使用 fplambda 表达式的问题,答案是否定的。来自hyperspec that deals with the evaluation of code的部分:"If the car of the compound form is not a symbol, then that car must be a lambda expression, in which case the compound form is a lambda form."。由于形式 (fplambda ...) 的汽车不是 lambda 表达式,因此您的代码不再是有效的 Common Lisp 代码。

我想出了一个解决方法,但有点难看。您可以定义一个 reader macro ,它允许您编写类似 ([fplambda ...] ...) 的内容并将其读取为

((LAMBDA (&REST #:G1030) (APPLY (FPLAMBDA ...) #:G1030)) ...)

哪个会做你想做的。这是允许您执行此操作的代码:

(set-macro-character #\[ 'bracket-reader)
(set-macro-character #\] (get-macro-character #\)))

(defun bracket-reader (stream char)
  "Read in a bracket."
  (declare (ignore char))
  (let ((gargs (gensym)))
    `(lambda (&rest ,gargs) 
       (apply ,(read-delimited-list #\] stream t)
              ,gargs))))

我能想到的唯一其他解决方案是使用某种代码遍历器(我帮不了你)。