【问题标题】:common lisp exception handling (condition and restart)common lisp 异常处理(条件和重启)
【发布时间】:2016-03-23 12:24:58
【问题描述】:

我已经阅读了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))
    

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

【问题讨论】:

    标签: exception common-lisp conditional-statements restart condition-system


    【解决方案1】:

    Practical Common Lisp 非常详细,但条件系统确实可能需要一些时间来适应。您可能会对 Kent Pitman 的文章感兴趣:Exceptional Situations in LispCondition Handling in the Lisp Language Family

    这些在What's a condition system and why do you want one? 中被引用。还有许多其他的参考资料,例如 Wikibooks entry 或 C2 wiki 的 CommonLispConditionSystem 条目。

    定义重启

    RESTART-CASE 基本上说:

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

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

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

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

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

    调用重启

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

    你应该做的是处理通过调用重启的错误。

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

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

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

    【讨论】:

    • 你提供的解决方案正是我想要的,但我仍然对重启的东西感到困惑,让我试着找到更多的材料来解决这个问题,谢谢!
    • @user2015063 如果用户有用,请考虑用绿色复选标记接受它。如果对您有帮助,我会添加更多参考资料。
    【解决方案2】:

    您需要将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)))
    

    【讨论】:

    • 感谢您的回复,我仍然不确定#'skip-evenp 指的是哪里。到?
    • #'SKIP-EVENP 指的是该名称的函数(这就是 #' 的含义)。 HANDLER-BIND 告诉它通过调用函数SKIP-EVENP 来处理任何EVENP-ERRORs。
    • @user2015063 #' 是一个 reader-macro:上面等价于 (function skip-event) 并使用 function 特殊运算符来引用在词法上绑定到 skip-event 符号的函数对象。
    • @jkiiski 非常感谢,我通过google搜索了更多示例后终于弄明白了,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 2020-12-02
    相关资源
    最近更新 更多