可以定义未命名的宏吗? ("lambda" 宏?)

Is defining unnamed macros possible? ("lambda" macros?)

我的目标是在编译时计算一个表达式,比如一些简单的东西,如 (+ 1 1)。但是,我希望在不使用 命名宏 的情况下编译一个整数“2”,而不是在运行时完成完整的“1+1”操作。这使我的源代码更清楚地显示了我如何获得值“2”,同时又不浪费 CPU 时间重复相同的“1+1”操作。这是一个简单的例子,说明了基本思想但不是真实案例,比如说,我理想的目标 defun 函数看起来像这样:

(defun test ()
   (+ 1 2 3))

我希望在编译时评估文字“2”所以我使用eval-when-compile:

(defun test ()
   (+ 1 
      (eval-when-compile (+ 1 1)) 
      3))

然而,结果变成:

(defun test ()
   (+ 1 '2 3))

对于这个简单的案例当然没问题,但是这个额外的 引用 会给更复杂的案例带来问题。

定义一个 命名宏 有效:

(defmacro 1+1 () `,(+ 1 1))
(defun test ()
   (+ 1 (1+1) 3))

它会产生我理想的结果,没有 quote 前缀整数“2”:

(defun test ()
    (+ 1 2 3))

有没有像上面eval-when-compile这样的简单方法来实现这个,在defun中?像一个未命名的宏来摆脱命名助手 1+1,或者我应该称之为 "lambda macro"?

您正在考虑不断折叠。不需要这样做,但最快的编译器会这样做。这是来自 SBCL 的示例:

* (defun test () (+ 1 2 3))

TEST
* (disassemble #'test)

; disassembly for TEST
; Size: 22 bytes. Origin: #x100189010C
; 0C:       498B4D60         MOV RCX, [R13+96]                ; no-arg-parsing entry point
                                                              ; thread.binding-stack-pointer
; 10:       48894DF8         MOV [RBP-8], RCX
; 14:       BA0C000000       MOV EDX, 12
; 19:       488BE5           MOV RSP, RBP
; 1C:       F8               CLC
; 1D:       5D               POP RBP
; 1E:       C3               RET
; 1F:       0F0B0F           BREAK 15                         ; Invalid argument count trap
NIL

你看不到任何加法,但你看到常量 12,它是 fixnum 6

的指针地址

在 CLISP 中:

[2]> (disassemble #'test)

Disassembly of function test
(CONST 0) = 6
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
2 byte-code instructions:
0     (const 0)                           ; 6
1     (skip&ret 1)
nil

因此,如果您使用现代 Common Lisp 实现,而不是有人尝试搞笑,您就不需要考虑这些问题。

我不知道有什么方法可以创建 匿名 宏,但是 macrolet 为命名宏提供了一个临时范围,您可以像这样使用它:

(defun foo ()
  (macrolet ((docstring () (concat "foo" "bar")))
    (docstring))
  'foo)

请注意,如果这是 真正的 仅用于文档字符串,您可以改为这样做:

(defun foo () 'foo)
(put 'foo 'function-documentation (concat "foo" "bar"))

受@phils 回答的启发,我终于能够定义匿名宏并 运行 它们。我定义了一个名为 immediate 的辅助宏来帮助解决这个问题;它基本上使用 macrolet 临时定义了一个匿名宏函数,然后 运行s 它:

(defmacro immediate (body)
  "Define an anonymous macro and run it immediately"
  (let ((f (make-symbol "immed")))
    `(macrolet ((,f () ,body))
       (,f))))

使用 immediate,我可以这样重写我原来问题中的示例代码:

(defun test ()
  (immediate (concat "Docstring" " of " "test" ))
  (+ 1
     (immediate (+ 1 1))
     3))

test 函数将完全按照我的预期定义:

(defun test ()
  "Docstring of test"
  (+ 1 2 3))

(名称 immediate 的灵​​感来自 Forth 语言的 immediate words。 )