不嵌套defun(使用flet或标签),sbcl REPL不报告所有条件(警告)

时间:2016-01-20 06:57:04

标签: common-lisp sbcl

在SBCL 1.3.1上进行的测试

我在函数xx中定义函数xxx。 (原来xx是递归的,并且在xxx闭包中使用了不变量。但是递归在这里并不重要,xx只返回txxx致电xx。因此,预计xxx也会返回t

xxx在函数call-xxx内被调用两次。要显示此问题(条件的那个),必须调用它两次。虽然这些电话之间没有共享状态,但为什么这很重要很奇怪。

call-xxx作为#'call-xxx传递给handler-case内的handler-case。它不需要参数,所以它只适用于nil。 xx将其拉出来说它会引发一种状况。

xxxww之外定义时,没有条件,并返回t的预期结果。这显示在下面显示的代码的第二部分中,其中我们有wwwcall-wwwhandler-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()
        }

2 个答案:

答案 0 :(得分:4)

代码有两个问题:

    Common Lisp中的
  1. defun定义了顶级定义。即使它出现在另一个函数内(这种方式与Scheme的define不同)。因此,每次调用xx时,都会重新定义函数xxx
  2. 条件condition甚至可以捕获“不值得一提”的内容(请参阅handler-case上的CLHS)。由于重新定义xx会产生警告,因此它会捕获它。如果不这样做,handler-case警告将显示在REPL中,但会产生正确的结果。

答案 1 :(得分:1)

我认为问题在于你无论如何都在拦截任何条件。在这种情况下,SBCL表示xxdefun重新定义的条件(不是错误,而是警告,因为它可能是无意的)。

将代码更改为:

(defun test (test-function)
  (handler-case (apply test-function '())
    (condition (x)
      (print x)
      ':exception)))

将显示#<SB-KERNEL:REDEFINITION-WITH-DEFUN {1002BE3E93}>

逻辑上的问题是你不应声明能够处理你不知道的条件:你应该处理你所知道的条件,并让其他人知道。只需让程序继续运行就可以通过适当的处理程序解决一个条件(就像这里的情况一样)。

这是条件和异常之间的主要区别:如果处理程序决定它是正确的事情,则仅在稍后执行展开。比如说C ++,一旦抛出异常,当处理异常时,所有已经在较低的调用堆栈级别丢失。