常见的 lisp 异常处理(条件和重启)
common lisp exception handling (condition and restart)
我已经阅读了 common lisp "Practical Common Lisp" 异常处理章节好几天了,但我现在对示例和解释感到困惑,同时我试图写一些测试示例,但没有成功'如我所料,下面是我的测试样本。
条件定义
(define-condition evenp-error (error)
((text :initarg :text :reader text)))
定义打印奇数的函数
(defun filter-evenp (lst)
(dolist (x lst)
(if (not (evenp x))
(print x)
(error 'evenp-error :text x))))
重启函数
(defun skip-evenp (c) (invoke-start 'skip-evenp))
重启案例
(restart-case (filter-evenp (list 1 2 3 4 5))
(skip-evenp () nil))
我只想打印所有奇数并跳过偶数错误,我的样本有什么问题?有人帮忙吗?非常感谢!!
您需要将 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)))
Practical Common Lisp is quite detailed but it is true that the condition system might require some time to get used to. You might be interested by Kent Pitman's articles: Exceptional Situations in Lisp and Condition Handling in the Lisp Language Family.
What's a condition system and why do you want one?. There are also many other references lilke this Wikibooks entry or C2 wiki's CommonLispConditionSystem 条目中引用了这些内容。
定义重启
A RESTART-CASE
基本上说:
I am going to execute this form and I don't care if it signals a condition or not. But if it does and you want to recover from that situation, here are different ways I can work around the problem (retry, ignore, etc.).
您通常无法说明如何从调用点调用的代码中的错误中恢复。换句话说,filter-evenp
应该用 restart-case
包装代码以提供替代路径。对于您的示例,使用 CERROR
就足够了,它会在建立 CONTINUE
重新启动时发出错误信号。
(if (evenp x)
(cerror "Ignore even number" 'evenp-error :text x)
(print x))
作为练习,您可以尝试用明确的 restart-case
构造替换 (cerror ...)
。
然后,如果您测试代码,您应该会看到调试器弹出并显示 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 提取到自定义函数中。
我已经阅读了 common lisp "Practical Common Lisp" 异常处理章节好几天了,但我现在对示例和解释感到困惑,同时我试图写一些测试示例,但没有成功'如我所料,下面是我的测试样本。
条件定义
(define-condition evenp-error (error) ((text :initarg :text :reader text)))
定义打印奇数的函数
(defun filter-evenp (lst) (dolist (x lst) (if (not (evenp x)) (print x) (error 'evenp-error :text x))))
重启函数
(defun skip-evenp (c) (invoke-start 'skip-evenp))
重启案例
(restart-case (filter-evenp (list 1 2 3 4 5)) (skip-evenp () nil))
我只想打印所有奇数并跳过偶数错误,我的样本有什么问题?有人帮忙吗?非常感谢!!
您需要将 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)))
Practical Common Lisp is quite detailed but it is true that the condition system might require some time to get used to. You might be interested by Kent Pitman's articles: Exceptional Situations in Lisp and Condition Handling in the Lisp Language Family.
What's a condition system and why do you want one?. There are also many other references lilke this Wikibooks entry or C2 wiki's CommonLispConditionSystem 条目中引用了这些内容。
定义重启
A RESTART-CASE
基本上说:
I am going to execute this form and I don't care if it signals a condition or not. But if it does and you want to recover from that situation, here are different ways I can work around the problem (retry, ignore, etc.).
您通常无法说明如何从调用点调用的代码中的错误中恢复。换句话说,filter-evenp
应该用 restart-case
包装代码以提供替代路径。对于您的示例,使用 CERROR
就足够了,它会在建立 CONTINUE
重新启动时发出错误信号。
(if (evenp x)
(cerror "Ignore even number" 'evenp-error :text x)
(print x))
作为练习,您可以尝试用明确的 restart-case
构造替换 (cerror ...)
。
然后,如果您测试代码,您应该会看到调试器弹出并显示 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 提取到自定义函数中。