5

我已经阅读了common lisp“Practical Common Lisp”异常处理章节好几天了,但是我现在对示例和解释感到很困惑,同时我尝试编写一些测试示例,但它没有像我一样工作预期,以下是我的测试样本。

  1. 条件定义

    (define-condition evenp-error (error) 
      ((text :initarg :text :reader text)))
    
  2. 定义打印奇数的函数

    (defun filter-evenp (lst)
      (dolist (x lst)
        (if (not (evenp x)) 
          (print x)
          (error 'evenp-error :text x))))
    
  3. 重启功能

    (defun skip-evenp (c) (invoke-start 'skip-evenp))
    
  4. 重启案例

    (restart-case (filter-evenp (list 1 2 3 4 5))
      (skip-evenp () nil))
    

我要做的就是打印所有奇数并跳过偶数错误,我的样本有什么问题?有人帮忙吗?提前谢谢了!!

4

2 回答 2

7

Practical Common Lisp非常详细,但条件系统确实可能需要一些时间来适应。您可能会对 Kent Pitman 的文章感兴趣: Lisp 中的异常情况Lisp 语言家族中的条件处理

什么是条件系统以及您为什么想要一个?. 还有许多其他参考资料,例如此Wikibooks 条目或 C2 wiki 的CommonLispConditionSystem条目。

定义重启

ARESTART-CASE基本上说:

我将执行此表单,我不在乎它是否表示条件。但是,如果确实如此,并且您想从这种情况中恢复过来,那么我可以通过不同的方式解决该问题(重试、忽略等)。

您通常无法说明如何从调用点调用的代码中的错误中恢复。换句话说,它filter-evenp应该用 a 包装代码restart-case以提供替代路径。对于您的示例,使用 就足够了,它会CERROR在建立重新启动时发出错误信号CONTINUE

(if (evenp x)
  (cerror "Ignore even number" 'evenp-error :text x) 
  (print x))

作为练习,您可以尝试(cerror ...)用显式restart-case构造替换。

然后,如果您测试您的代码,您应该会看到调试器弹出并显示CONTINUE重新启动。如果您定义了自己的重新启动,则可以将其命名为不同的名称。

调用重启

在您的skip-evenp函数中,您正在调用此时尚未建立的重新启动,我认为您对同时skip-evenp命名重新启动和函数感到困惑。

您应该做的是通过调用重新启动来处理错误。

在这里,您希望发出错误信号的代码继续执行,因此您真的不想展开执行堆栈。这就是为什么你必须使用HANDLER-BIND.

(handler-bind ((evenp-error (lambda (e) (invoke-restart 'continue))))
  (filter-evenp '(1 2 3 4)))
1
3    

您当然可以像您一样将匿名 lambda 提取到自定义函数中。

于 2016-03-23T12:56:22.643 回答
5

您需要将您RESTART-CASE的放置在您想要重新开始执行的位置:

(defun filter-evenp (lst)
  (dolist (x lst)
    (restart-case
        (if (not (evenp x))
            (print x)
            (error 'evenp-error :text x))
      (skip-evenp () nil))))

然后你应该使用HANDLER-BIND来处理错误:

(handler-bind ((evenp-error #'skip-evenp))
  (filter-evenp (list 1 2 3 4 5)))
于 2016-03-23T12:52:07.583 回答