有什么方法可以查看 Common Lisp 中内置宏的实现吗?
Is there any way to see the implementations of built-in macros in Common Lisp?
Common Lisp 内置函数可能是用 C 实现的。但我想宏是用 lisp 实现的(如果我对两句话中的任何一个有误,抱歉)。有没有办法(通过一些函数或一些宏)查看 Common Lisp 中内置宏的实现?我正在使用 CLisp。
检查函数和宏定义的能力是您的开发环境的一项功能。现在通常使用 SLIME or SLY 和 emacs 作为 Lisp 开发环境的基础。我个人使用 SLIME,但我也听说过 SLY 的好消息。
在 SLIME 中,您可以调用 slime-edit-definition
(通过键入 M-x slime-edit-definition
或使用键绑定 M-.
)来访问源文件中光标下符号的定义。这在编辑源文件或 REPL 时都有效。当您想检查正在使用的某些库代码时,此功能非常有用,但您也可以通过这种方式查看许多内置定义。您甚至可以从当前正在检查的任何定义中找到的新符号跳转到新定义。
看完定义后,您可以使用 M-x slime-pop-find-definition-stack
或更容易记住的键绑定 M-,
(M-*
也可以),通过以前查看过的定义,最终会回到您的起点。
这是一个示例,在 SBCL 中:
CL-USER> with-open-file[press M-.]
(注意上面的“[按M-.]”不是打出来的,只是提醒这里有什么操作)。将光标放在符号 with-open-file
上或其后,按 M-.
查看定义:
(sb-xc:defmacro with-open-file ((stream filespec &rest options)
&body body)
(multiple-value-bind (forms decls) (parse-body body nil)
(let ((abortp (gensym)))
`(let ((,stream (open ,filespec ,@options))
(,abortp t))
,@decls
(unwind-protect
(multiple-value-prog1
(progn ,@forms)
(setq ,abortp nil))
(when ,stream
(close ,stream :abort ,abortp)))))))
这一次在键入 M-.
后,SLIME 提供了要查看的定义选择:
CL-USER> and[press M-.]
在 emacs 缓冲区中显示:
/path-to-source/sbcl-2.0.4/src/code/macros.lisp
(DEFMACRO AND)
/path-to-source/sbcl-2.0.4/src/pcl/ctypes.lisp
(DEFINE-METHOD-COMBINATION AND)
我们想看宏定义,所以将光标移动到显示(DEFMACRO AND)
的行,显示如下定义:
;; AND and OR are defined in terms of IF.
(sb-xc:defmacro and (&rest forms)
(named-let expand-forms ((nested nil) (forms forms) (ignore-last nil))
(cond ((endp forms) t)
((endp (rest forms))
(let ((car (car forms)))
(cond (nested
car)
(t
;; Preserve non-toplevelness of the form!
`(the t ,car)))))
((and ignore-last
(endp (cddr forms)))
(car forms))
;; Better code that way, since the result will only have two
;; values, NIL or the last form, and the precedeing tests
;; will only be used for jumps
((and (not nested) (cddr forms))
`(if ,(expand-forms t forms t)
,@(last forms)))
(t
`(if ,(first forms)
,(expand-forms t (rest forms) ignore-last))))))
这里还有更多内容,因为您现在实际上位于包含 and
定义的源文件中;如果向下滚动一点,您还可以找到 or
.
的定义
很多SBCL函数都是用Lisp写的; SBCL 有一个非常高质量的编译器,所以很多你可能期望用 C 写的东西可以用 Lisp 写,而不会损失性能。这是函数 list-length
:
的定义
CL-USER> list-length[press M-.]
(defun list-length (list)
"Return the length of the given List, or Nil if the List is circular."
(do ((n 0 (+ n 2))
(y list (cddr y))
(z list (cdr z)))
(())
(declare (type fixnum n)
(type list y z))
(when (endp y) (return n))
(when (endp (cdr y)) (return (+ n 1)))
(when (and (eq y z) (> n 0)) (return nil))))
将 CLISP 与 SLIME 一起使用时也可以完成同样的事情。这是 CLISP 中定义的 with-open-file
:
CL-USER> with-open-file[press M-.]
(defmacro with-open-file ((stream &rest options) &body body)
(multiple-value-bind (body-rest declarations) (SYSTEM::PARSE-BODY body)
`(LET ((,stream (OPEN ,@options)))
(DECLARE (READ-ONLY ,stream) ,@declarations)
(UNWIND-PROTECT
(MULTIPLE-VALUE-PROG1
(PROGN ,@body-rest)
;; Why do we do a first CLOSE invocation inside the protected form?
;; For reliability: Because the stream may be a buffered file stream,
;; therefore (CLOSE ,stream) may produce a disk-full error while
;; writing the last block of the file. In this case, we need to erase
;; the file again, through a (CLOSE ,stream :ABORT T) invocation.
(WHEN ,stream (CLOSE ,stream)))
(WHEN ,stream (CLOSE ,stream :ABORT T))))))
但是,许多 CLISP 函数是用 C 语言编写的,这些定义无法像以前那样进行检查:
CL-USER> list-length[press M-.]
No known definition for: list-length (in COMMON-LISP-USER)
Common Lisp 内置函数可能是用 C 实现的。但我想宏是用 lisp 实现的(如果我对两句话中的任何一个有误,抱歉)。有没有办法(通过一些函数或一些宏)查看 Common Lisp 中内置宏的实现?我正在使用 CLisp。
检查函数和宏定义的能力是您的开发环境的一项功能。现在通常使用 SLIME or SLY 和 emacs 作为 Lisp 开发环境的基础。我个人使用 SLIME,但我也听说过 SLY 的好消息。
在 SLIME 中,您可以调用 slime-edit-definition
(通过键入 M-x slime-edit-definition
或使用键绑定 M-.
)来访问源文件中光标下符号的定义。这在编辑源文件或 REPL 时都有效。当您想检查正在使用的某些库代码时,此功能非常有用,但您也可以通过这种方式查看许多内置定义。您甚至可以从当前正在检查的任何定义中找到的新符号跳转到新定义。
看完定义后,您可以使用 M-x slime-pop-find-definition-stack
或更容易记住的键绑定 M-,
(M-*
也可以),通过以前查看过的定义,最终会回到您的起点。
这是一个示例,在 SBCL 中:
CL-USER> with-open-file[press M-.]
(注意上面的“[按M-.]”不是打出来的,只是提醒这里有什么操作)。将光标放在符号 with-open-file
上或其后,按 M-.
查看定义:
(sb-xc:defmacro with-open-file ((stream filespec &rest options)
&body body)
(multiple-value-bind (forms decls) (parse-body body nil)
(let ((abortp (gensym)))
`(let ((,stream (open ,filespec ,@options))
(,abortp t))
,@decls
(unwind-protect
(multiple-value-prog1
(progn ,@forms)
(setq ,abortp nil))
(when ,stream
(close ,stream :abort ,abortp)))))))
这一次在键入 M-.
后,SLIME 提供了要查看的定义选择:
CL-USER> and[press M-.]
在 emacs 缓冲区中显示:
/path-to-source/sbcl-2.0.4/src/code/macros.lisp
(DEFMACRO AND)
/path-to-source/sbcl-2.0.4/src/pcl/ctypes.lisp
(DEFINE-METHOD-COMBINATION AND)
我们想看宏定义,所以将光标移动到显示(DEFMACRO AND)
的行,显示如下定义:
;; AND and OR are defined in terms of IF.
(sb-xc:defmacro and (&rest forms)
(named-let expand-forms ((nested nil) (forms forms) (ignore-last nil))
(cond ((endp forms) t)
((endp (rest forms))
(let ((car (car forms)))
(cond (nested
car)
(t
;; Preserve non-toplevelness of the form!
`(the t ,car)))))
((and ignore-last
(endp (cddr forms)))
(car forms))
;; Better code that way, since the result will only have two
;; values, NIL or the last form, and the precedeing tests
;; will only be used for jumps
((and (not nested) (cddr forms))
`(if ,(expand-forms t forms t)
,@(last forms)))
(t
`(if ,(first forms)
,(expand-forms t (rest forms) ignore-last))))))
这里还有更多内容,因为您现在实际上位于包含 and
定义的源文件中;如果向下滚动一点,您还可以找到 or
.
很多SBCL函数都是用Lisp写的; SBCL 有一个非常高质量的编译器,所以很多你可能期望用 C 写的东西可以用 Lisp 写,而不会损失性能。这是函数 list-length
:
CL-USER> list-length[press M-.]
(defun list-length (list)
"Return the length of the given List, or Nil if the List is circular."
(do ((n 0 (+ n 2))
(y list (cddr y))
(z list (cdr z)))
(())
(declare (type fixnum n)
(type list y z))
(when (endp y) (return n))
(when (endp (cdr y)) (return (+ n 1)))
(when (and (eq y z) (> n 0)) (return nil))))
将 CLISP 与 SLIME 一起使用时也可以完成同样的事情。这是 CLISP 中定义的 with-open-file
:
CL-USER> with-open-file[press M-.]
(defmacro with-open-file ((stream &rest options) &body body)
(multiple-value-bind (body-rest declarations) (SYSTEM::PARSE-BODY body)
`(LET ((,stream (OPEN ,@options)))
(DECLARE (READ-ONLY ,stream) ,@declarations)
(UNWIND-PROTECT
(MULTIPLE-VALUE-PROG1
(PROGN ,@body-rest)
;; Why do we do a first CLOSE invocation inside the protected form?
;; For reliability: Because the stream may be a buffered file stream,
;; therefore (CLOSE ,stream) may produce a disk-full error while
;; writing the last block of the file. In this case, we need to erase
;; the file again, through a (CLOSE ,stream :ABORT T) invocation.
(WHEN ,stream (CLOSE ,stream)))
(WHEN ,stream (CLOSE ,stream :ABORT T))))))
但是,许多 CLISP 函数是用 C 语言编写的,这些定义无法像以前那样进行检查:
CL-USER> list-length[press M-.]
No known definition for: list-length (in COMMON-LISP-USER)