common-lisp中有关可选参数的错误

时间:2013-05-30 13:20:58

标签: common-lisp sbcl optional-arguments ansi-common-lisp

SBCL 64bit,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,那么如何解决这个问题呢?感谢。

3 个答案:

答案 0 :(得分: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符号。

答案 1 :(得分:6)

有趣的是,这可能表明与SBCL和大多数其他实施方案存在明显差异。

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

因此,这会与GNU CLISP产生这种时髦的差异:

&optional是所谓的 lambda list关键字

这是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实现),因为默认使用&#34; CL&#34;包。 SBCL不使用 CL包。如果您未指定要使用的程序包,则SBCL 使用 none。大多数其他实现创建一个新包,其中包含一组默认的有用包继承。 SBCL开发人员认为这是特别聪明,但暴露了不兼容性 - 我认为这根本不是那么聪明。该标准允许对DEFPACKAGE进行SBCL解释,但已将其更改为此,因为知道其他实现不会这样做。

在GNU CLISP(以及大多数其他CL实现)中,import语句没有效果,因为包使用了所有包&#34; CL&#34;反正。

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 list关键字导入你的包中。

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

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

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

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

答案 2 :(得分:4)

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

一个选项是将:&optional :&rest添加到从CL包导入的符号列表中,另一个选项是整个销售导入它,同时遮蔽一些您想要重新定义的符号。