Common lisp 不要嵌套defun(使用flet或标签),sbcl REPL不会报告所有情况(警告)
对SBCL 1.3.1进行的试验 我在函数的内部定义函数Common lisp 不要嵌套defun(使用flet或标签),sbcl REPL不会报告所有情况(警告),common-lisp,sbcl,Common Lisp,Sbcl,对SBCL 1.3.1进行的试验 我在函数的内部定义函数xx。(最初xx是递归的,并且从xxx闭包中使用了不变量。但是递归在这里并不重要,xx只返回t)xxx只调用xx。因此,xxx也将返回t xxx在函数内部被调用两次call xxx。要使此问题(条件的问题)显化,必须调用两次。虽然调用之间没有共享状态,但奇怪的是,为什么这会很重要 call xxx作为#“call-xxx传递,在处理程序案例中应用。它不需要参数,所以只应用于nil处理程序案例将其拉出并表示它抛出了一个条件 如果在xxx之外定
xx
。(最初xx
是递归的,并且从xxx
闭包中使用了不变量。但是递归在这里并不重要,xx
只返回t
)xxx
只调用xx
。因此,xxx
也将返回t
xxx
在函数内部被调用两次call xxx
。要使此问题(条件的问题)显化,必须调用两次。虽然调用之间没有共享状态,但奇怪的是,为什么这会很重要
call xxx
作为#“call-xxx
传递,在处理程序案例中应用。它不需要参数,所以只应用于nil<代码>处理程序案例
将其拉出并表示它抛出了一个条件
如果在xxx
之外定义了xx
,则不存在任何条件,并返回t的预期结果。下面代码的第二部分显示了这一点,其中我们有ww
,www
,调用www
,等等。同样,当未使用处理程序案例时,REPL也不会报告任何异常
[此示例从测试框架中删除,因此当调用xxx
被称为抛出异常时,测试失败。然而,当测试手动运行时(请参见运行调用xx
),它会毫无异常地通过,从而产生矛盾,并使调试明显失败的测试变得困难。]
是什么导致这种情况?条件处理程序调用是否应该不同?这是SBCL错误吗
代码如下:
(defun test (test-function)
(handler-case (apply test-function '()) (condition () ':exception))
;;; (apply test-function '()) ;; returns t, no exception
)
;;---------------------------------------------------------------
;; throws exception, but shouldn't (?)
;;
(defun xxx ()
(defun xx () t) ; note comments, should be a flet or labels form
(xx))
(defun call-xxx ()
(xxx) ;; #'xxx must be called twice for the exception to appear
(xxx)
t)
;; call-xxx throws exception when run from test, but shouldn't
(defun run-test-call-xxx ()
(test #'call-xxx))
;; no problem here, call-xxx returns t when called directly
(defun run-call-xxx ()
(call-xxx))
;;--------------------------------------------------------
;; works fine
;; pulled out the nested definition of #'ww from #'www
;;
(defun ww () t)
(defun www ()
(ww))
(defun call-www ()
(www)
(www)
t)
(defun run-test-call-www ()
(test #'call-www))
现在它正在运行:
§sbcl> sbcl
This is SBCL 1.3.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (load "src/test-xxx")
T
* (run-test-call-xxx)
:EXCEPTION
* (run-test-call-www)
T
*
这就是REPL中发生的情况:
§sbcl> sbcl
This is SBCL 1.3.1.debian, an implementation of ANSI Common Lisp....
distribution for more information.
* (load "src/test-handler.lisp")
T
* (run-test-call-xxx)
T
*
如您所见,REPL不会打印警告
注意注释,defun
只是顶层,因此使用flet
处理闭包中的递归。使用defun
时,将在每个条目上定义内部函数,尽管发出了警告,但该警告不会发送到REPL(尽管处理程序案例确实看到了它,这就是这里发生的情况)
代码有两个问题:
(defun test (test-function)
(handler-case (apply test-function '()) (condition () ':exception))
;;; (apply test-function '()) ;; returns t, no exception
)
;;---------------------------------------------------------------
;; throws exception, but shouldn't (?)
;;
(defun xxx ()
(defun xx () t) ; note comments, should be a flet or labels form
(xx))
(defun call-xxx ()
(xxx) ;; #'xxx must be called twice for the exception to appear
(xxx)
t)
;; call-xxx throws exception when run from test, but shouldn't
(defun run-test-call-xxx ()
(test #'call-xxx))
;; no problem here, call-xxx returns t when called directly
(defun run-call-xxx ()
(call-xxx))
;;--------------------------------------------------------
;; works fine
;; pulled out the nested definition of #'ww from #'www
;;
(defun ww () t)
(defun www ()
(ww))
(defun call-www ()
(www)
(www)
t)
(defun run-test-call-www ()
(test #'call-www))
公共Lisp中的defun
定义了顶级定义。即使它出现在另一个函数中(这种方式不同于Scheme的define
)。因此,每次调用xxx
时都会重新定义函数xx
条件Condition
甚至会捕获一些“不值得一提”的内容(请参见handler case
)。由于重新定义xx
会产生警告,因此它将捕获该警告。如果不执行此操作,则REPL中将显示处理程序案例
警告,但会生成正确的结果
我认为这里的问题是,无论发生什么情况,你都在拦截任何条件。在这种情况下,SBCL会发出一个条件,即使用defun
重新定义xx
(不是错误,而是一个警告,因为这可能是意外的)
将代码更改为:
(defun test (test-function)
(handler-case (apply test-function '())
(condition (x)
(print x)
':exception)))
将显示#
逻辑上的问题是,你不应该声明能够处理你不知道的条件:你应该处理你知道的条件,让其他人由谁知道的人来处理。适当的处理程序可以通过让程序继续运行来解决问题(这里就是这种情况)
这是条件和异常之间的主要区别:只有在处理程序确定这是正确的操作时,才会在稍后执行解除。用C++来代替,一旦抛出异常,在处理异常时,所有的调用堆栈级别都已经丢失。< / P>。在这一点中,正确定义另一个函数的方法是使用<代码>标签< /> >或<代码> FLe> <代码>。它们的工作原理很像let
,但使用函数而不是变量。REPL中不会显示警告-如果它有我的帖子,则会有所不同。是否有SBCL开关或我应该抛出的东西来查看此警告?我已经编辑添加了上面的成绩单。杰基斯基已经预料到了我的下一个问题。(哇,非常好的信息,谢谢!)@user244488老实说,我不知道警告开关。我的粘液默认设置产生;SLIME 2015-06-01 STYLE-WARNING:在DEFUN中重新定义COMMON-LISP-USER::XX和在REPL中重新定义COMMON-LISP-USER::TEST.你是MOSTEK人吗?是的,谢谢你上面的打印表格,这使它更清楚了。这是将进入测试台的修复。测试台正确地指出了原始代码存在问题(我只是不理解发生了什么),因为在每次调用时重新定义函数不是一件好事。但是,难道测试台不适合捕捉所有东西吗?
(defun test (test-function)
(handler-case (apply test-function '())
(condition (x)
(print x)
':exception)))