如标题所述,我想打印我的整个功能。
(DEFUN X () ...)
-> (DEFUN X () ...)
我需要在“...”中写什么?
#1=(defun x () (write '#1# :circle t))
Lars 的回答很聪明,使用循环结构来引用源中的源。另一个可能对一般内省目的更有用的选项是定义一个特殊变量,该变量提供对正在定义的表单的访问。这是一个初始但不是很完善的版本:
(defpackage #:introspective-common-lisp
(:use "COMMON-LISP")
(:shadow "DEFUN")
(:nicknames #:icl))
(in-package #:icl)
(defvar *current-form* nil
"The current form being evaluated (typically a definition form.")
(defmacro defun (&whole form name lambda-list &body body)
"Like CL:DEFUN, except that within BODY, *CURRENT-FORM* is bound to
the defining ICL:DEFUN form."
`(cl:defun ,name ,lambda-list
(let ((*current-form* ',form))
,@body)))
(defun x ()
"A function that prints its source."
(print *current-form*))
CL-USER> (in-package #:icl)
#<PACKAGE "INTROSPECTIVE-COMMON-LISP">
ICL> (x)
(DEFUN X
NIL
(PRINT *CURRENT-FORM*))
(DEFUN X
NIL
(PRINT *CURRENT-FORM*))
请记住,NIL
和()
在 Common Lisp 中(defun x () ...)
是一样的,所以和(defun x nil ...)
. 当然,您可以检查 的值*current-form*
并决定打印()
,但这里的重点是您可以访问该表单,并且您现在可以随意打印它(或用它做任何您想做的事情) )。
使用 Common Lisp 的宏工具,这实际上是一件非常容易的事情,而且我能够在很短的时间内将它拼凑在一起。但是,有一些微妙之处需要注意。在这个初始版本中,我将自定义icl:defun
宏扩展为
`(cl:defun ,name ,lambda-list
(let ((*current-form* ',form))
,@body)))
但是,这将放错来自body
的声明。这真的需要是这样的:
`(cl:defun ,name ,lambda-list
,@(util:body-declarations body)
(let ((*current-form* ',form))
,@(util:body-forms body)))
有一些包可以将正文解析为声明/文档字符串和表单,而且你自己的也不太难。
您也可以let
完全跳过,并在 的 lambda 列表中添加一个&aux
变量cl:defun
,但要做到这一点,您需要检查&aux
lambda 列表中是否已经有一个关键字,因为您不想添加多余的一个。这并不难做到,但它确实使我们的代码更复杂了一点:
(eval-when (:compile-toplevel :load-toplevel :execute)
(cl:defun with-aux-variable (lambda-list name &optional value)
"Returns a new lambda list like LAMBDA-LIST (which should be an
ordinary lambda list), but with an NAME as an &aux variable with the
specified VALUE."
(let ((tail (if (member '&aux lambda-list)
(list (list name value))
(list '&aux (list name value)))))
(append lambda-list tail)))
) ; eval-when
(defmacro defun (&whole form name lambda-list &body body)
"Like CL:DEFUN, except that within BODY, *CURRENT-FORM* is bound to
the defining ICL:DEFUN form."
`(cl:defun ,name ,(with-aux-variable lambda-list
'*current-form*
(list 'quote form))
,@body))