可以定义未命名的宏吗? ("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。 )
我的目标是在编译时计算一个表达式,比如一些简单的东西,如 (+ 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。 )