5

SBCL 64 位,1.1.7

如果我想创建一个包并使用包中的一些符号:CL,我将创建一个像这样的包:

(defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))

但是,在这个包中,如果我定义一个带有可选参数的函数并在没有提供可选参数的情况下调用它,我总是会得到一个错误:

(defun test (&optional a))

(test)

invalid number of arguments: 0
   [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]

Restarts:
 0: [RETRY] Retry SLIME interactive evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 10: #<CLOSURE (COMMON-LISP:LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {100490B95B}>>
 3: [ABORT] Exit debugger, returning to top level.

定义一个宏得到同样的错误,但有更多信息:

(defmacro test (&rest body))

(test)

error while parsing arguments to DEFMACRO TEST:
  invalid number of elements in
    ()
  to satisfy lambda list
    (&REST BODY):
  exactly 2 expected, but 0 found
   [Condition of type SB-KERNEL::ARG-COUNT-ERROR]

我想可能是因为缺少 :CL 中的一些符号,那么如何解决这个问题呢?谢谢。

4

3 回答 3

7

我相信这将对问题有所了解;)

CL-USER> (defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))
#<PACKAGE "FOO">
CL-USER> (in-package :foo)
#<COMMON-LISP:PACKAGE "FOO">
FOO> (defun bar (&optional baz))
; in: DEFUN BAR
;     (SB-INT:NAMED-LAMBDA FOO::BAR
;         (FOO::&OPTIONAL FOO::BAZ)
;       (BLOCK FOO::BAR))
; 
; caught COMMON-LISP:STYLE-WARNING:
;   suspicious variable in lambda list: &OPTIONAL.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   suspicious variable in lambda list: &OPTIONAL.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable &OPTIONAL is defined but never used.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable BAZ is defined but never used.
; 
; compilation unit finished
;   caught 4 STYLE-WARNING conditions
BAR
FOO> (in-package :cl)
#<PACKAGE "COMMON-LISP">
CL> (defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package :&optional
                :null :car :cdr :cons :if
                :eq))
Select a symbol to be made accessible in package FOO:
  1. COMMON-LISP:&OPTIONAL
  2. FOO::&OPTIONAL

Enter an integer (between 1 and 2): 1

#<PACKAGE "FOO">
CL> (in-package :foo)
#<COMMON-LISP:PACKAGE "FOO">
FOO> (defun bar (&optional baz))
; in: DEFUN BAR
;     (SB-INT:NAMED-LAMBDA FOO::BAR
;         (&OPTIONAL FOO::BAZ)
;       (BLOCK FOO::BAR))
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable BAZ is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition
COMMON-LISP:STYLE-WARNING: redefining FOO::BAR in DEFUN
BAR
FOO> 

&optional&rest只是像其他任何符号一样的符号,您也需要导入它们。但是,也许这不是从cl包中导入的最佳方式......除非您确定这是您需要的。以防万一:您可以:use使用整个包装而不是:import-from逐个符号。

于 2013-05-30T13:40:21.213 回答
6

有趣的是,这可能指向与 SBCL 和大多数其他实现的模糊差异。

如果我们在 SBCL 中创建一个新包,该包默认不使用任何其他包。

因此,这导致了与 GNU CLISP 的这种时髦差异:

&optional是一个所谓的lambda 列表关键字

这是 GNU CLISP:

[1]> (defpackage :foo
               (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))
#<PACKAGE FOO>
[2]> (in-package "FOO")
#<PACKAGE FOO>
FOO[3]> (defun test (&optional a))
TEST
FOO[4]> (test)
NIL

它适用于 CLISP(和大多数其他 CL 实现),因为它默认使用“CL”包。SBCL 不使用CL软件包。如果您不指定要使用的包,SBCL将不使用任何包。大多数其他实现创建一个新的包,其中包含一组默认的有用包来继承。SBCL 开发人员认为这特别聪明,但暴露了不兼容性——我认为这一点也不聪明。该标准允许对 SBCL 进行解释DEFPACKAGE,但在知道其他实现不这样做的情况下将其更改为此。

在 GNU CLISP(和大多数其他 CL 实现)中,您的 import 语句没有效果,因为该包无论如何都使用所有包“CL”。

SBCL:

* (defpackage "BAR")

#<PACKAGE "BAR">

* (package-use-list "BAR")

NIL

将其与 CLISP 进行比较:

[1]> (defpackage "BAR")
#<PACKAGE BAR>
[2]> (package-use-list "BAR")
(#<PACKAGE COMMON-LISP>)

因此,您需要将 SBCL 中的 lambda 列表关键字导入到您的包中。

这也意味着您需要像这样编写包声明:

(defpackage :foo
  (:import-from :cl 
   :defun :defmacro :in-package
   :null :car :cdr :cons :if
   :eq
   :&optional)
  (:use))

上面添加了&optional lambda list 关键字。您可能还想添加其他lambda 列表关键字

它还明确指定不使用任何包。

于 2013-05-30T14:38:22.037 回答
4

如果您在包 FOO 中尝试以下(cl:symbol-package '&optional)操作,您可以看到使用了哪个包。Common Lisp 期望cl:&optional在 lambda 列表中使用它,但您正在使用foo::&optional它,因为它是一个没有语义意义的符号,它只是一个与其他任何参数一样的参数。

一种选择是添加:&optional :&rest到您从 CL 包中导入的符号列表中,另一种选择是批发导入它,同时隐藏一些您想要重新定义的符号。

于 2013-05-30T14:03:41.537 回答