在SBCL 1.3.1上进行的测试
我在函数xx
中定义函数xxx
。 (原来xx
是递归的,并且在xxx
闭包中使用了不变量。但是递归在这里并不重要,xx
只返回t
)xxx
致电xx
。因此,预计xxx
也会返回t
。
xxx
在函数call-xxx
内被调用两次。要显示此问题(条件的那个),必须调用它两次。虽然这些电话之间没有共享状态,但为什么这很重要很奇怪。
call-xxx
作为#'call-xxx
传递给handler-case
内的handler-case
。它不需要参数,所以它只适用于nil。 xx
将其拉出来说它会引发一种状况。
当xxx
在ww
之外定义时,没有条件,并返回t的预期结果。这显示在下面显示的代码的第二部分中,其中我们有www
,call-www
,handler-case
等。当不使用call-xxx
时,没有REPL报告的异常。
[此示例从测试框架中删除,因此当run-call-xx
被认为抛出异常时,测试失败。然而,当测试手动运行时(参见(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> 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
*
这里正在运行:
(defun test (test-function)
;;; -> this does not get handled
;;; (handler-case (apply test-function '()) (serious-condition () ':exception))
;;; -> this triggers the handler
;;; (handler-case (apply test-function '()) (condition () ':exception))
(apply test-function '()))
添加这里是重新定义的测试函数,用于在没有处理程序的情况下进行回复。我这样做是为了看看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
如您所见,REPL不会打印警告。
注意注释,flet
只是顶级,因此使用defun
处理闭包内的递归。使用(defun f (s too-big-for-stack-invariant)
(defun r (s) ;; note comments, should be a labels form, not defun
;; changes s and checks for termination
;; makes use of the very large invariant data
...
r(s))
;; some stuff making use of r(s)
)
,内部函数将在每个条目上定义,但会发出警告,但不会将其发送到REPL(虽然处理程序的情况确实看到了,这就是这里发生的事情)
val realm = Realm.getInstance(this)
val allUsers = realm.where(User::class.java).findAll()
val deletedDependent = realm.where(User::class.java).equalTo("id",deleteDependentData.dependentUUID).findFirst()
try {
realm.beginTransaction()
deletedDependent.removeFromRealm()
realm.commitTransaction()
} catch (e: RealmException) {
info("Error deleting realm object=>${e.message}")
e.printStackTrace()
}
答案 0 :(得分:4)
代码有两个问题:
defun
定义了顶级定义。即使它出现在另一个函数内(这种方式与Scheme的define
不同)。因此,每次调用xx
时,都会重新定义函数xxx
。condition
甚至可以捕获“不值得一提”的内容(请参阅handler-case
上的CLHS)。由于重新定义xx
会产生警告,因此它会捕获它。如果不这样做,handler-case
警告将显示在REPL中,但会产生正确的结果。答案 1 :(得分:1)
我认为问题在于你无论如何都在拦截任何条件。在这种情况下,SBCL表示xx
被defun
重新定义的条件(不是错误,而是警告,因为它可能是无意的)。
将代码更改为:
(defun test (test-function)
(handler-case (apply test-function '())
(condition (x)
(print x)
':exception)))
将显示#<SB-KERNEL:REDEFINITION-WITH-DEFUN {1002BE3E93}>
逻辑上的问题是你不应声明能够处理你不知道的条件:你应该处理你所知道的条件,并让其他人知道。只需让程序继续运行就可以通过适当的处理程序解决一个条件(就像这里的情况一样)。
这是条件和异常之间的主要区别:如果处理程序决定它是正确的事情,则仅在稍后执行展开。比如说C ++,一旦抛出异常,当处理异常时,所有已经在较低的调用堆栈级别丢失。