Prolog 是否有条件并像 Common Lisp 那样重启系统? [英] Does Prolog have a condition and restart system like Common Lisp?

查看:41
本文介绍了Prolog 是否有条件并像 Common Lisp 那样重启系统?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Common Lisp 允许通过条件和重启进行异常处理.粗略地说,当一个函数抛出异常时,捕获者"可以决定抛出者"应该如何/是否继续进行.Prolog 是否提供类似的系统?如果不是,是否可以在现有谓词之上构建一个来遍历和检查调用堆栈?

解决方案

Prolog 的 ISO/IEC 标准只提供了一个非常基本的异常和错误处理机制,它或多或少地与 Java 提供的相当,与 Common Lisp 的丰富机制相去甚远,但仍有一些值得注意的地方.特别是,除了实际的信令和处理机制,许多系统还提供了类似于 unwind-protect 的机制.也就是说,即使存在未处理的信号,也能确保目标得到执行的方法.

ISO 投掷/1,接住/3

使用 throw(Term) 引发/抛出异常.首先使用 copy_term/2 创建 Term 的副本,我们称之为 Termcopy 然后这个新副本用于搜索相应的 catch(Goal, Pattern, Handler) 其第二个参数与 Termcopy 统一.当 Handler 被执行时,所有由 Goal 引起的统一都会被撤销.因此,当 throw/1 执行时,Handler 无法访问存在的替换.并且没有办法在throw/1被执行的地方继续.

内置谓词的错误通过执行 throw(error(Error_term, Imp_def)) 发出信号,其中 Error_term 对应于 ISO 的错误类Imp_def 可能提供实现定义的额外信息(如源文件、行号等).

在很多情况下,在本地处理错误会有很大的好处,但许多实现者认为它太复杂而无法实现.

使 Prolog 处理器在本地处理每个错误的额外工作是相当可观的,并且比 Common Lisp 或其他编程语言要大得多.这是由于 Prolog 中统一的本质.错误的本地处理需要撤消在执行内置函数期间执行的统一:因此,实现者有两种可能性来实现这一点:

  • 在调用内置谓词时创建一个选择点",这会产生大量额外的开销,无论是创建这个选择点还是尾随"后续绑定
  • 手动检查每个内置谓词,并根据具体情况决定如何处理错误 —虽然这是在运行时开销方面最有效的方法,但这也是最昂贵和最容易出错的方法

利用内置程序中的 WAM 寄存器会导致类似的复杂性.同样,人们可以在缓慢的系统或具有大量实施开销的系统之间做出选择.

exception_handler/3

然而,许多系统在内部提供更好的机制,但很少有人始终如一地向程序员提供这些机制.IF/Prolog 提供了 exception_handler/3,它与 catch/3 具有相同的参数,但在本地处理错误或异常:

<预>[用户] ?- catch((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).不[用户] ?- exception_handler((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).Z = 好的是的

setup_call_cleanup/3

很多系统都提供了这个内置功能.它与 unwind-protect 非常相似,但由于 Prolog 的回溯机制需要一些额外的复杂性.查看其当前定义.

<小时>

所有这些机制都需要由系统实现者提供,它们不能建立在 ISO Prolog 之上.

Common Lisp allows exception handling through conditions and restarts. In rough terms, when a function throws an exception, the "catcher" can decide how/whether the "thrower" should proceed. Does Prolog offer a similar system? If not, could one be built on top of existing predicates for walking and examining the call stack?

解决方案

The ISO/IEC standard of Prolog provides only a very rudimentary exception and error handling mechanism which is - more or less - comparable to what Java offers and far away from Common Lisp's rich mechanism, but there are still some points worth noting. In particular, beside the actual signalling and handling mechanism, many systems provide a mechanism similar to unwind-protect. That is, a way to ensure that a goal will be executed, even in the presence of otherwise unhandled signals.

ISO throw/1, catch/3

An exception is raised/thrown with throw(Term). First a copy of Term is created with copy_term/2 lets call it Termcopy and then this new copy is used to search for a corresponding catch(Goal, Pattern, Handler) whose second argument unifies with Termcopy. When Handler is executed, all unifications caused by Goal are undone. So there is no way for the Handler to access the substitutions present when throw/1 is executed. And there is no way to continue at the place where the throw/1 was executed.

Errors of built-in predicates are signaled by executing throw(error(Error_term, Imp_def)) where Error_term corresponds to one of ISO's error classes and Imp_def may provide implementation defined extra information (like source file, line number etc).

There are many cases where handling an error locally would be of great benefit but it is deemed by many implementors to be too complex to implement.

The additional effort to make a Prolog processor handle each and every error locally is quite considerable and is much larger than in Common Lisp or other programming languages. This is due to the very nature of unification in Prolog. The local handling of an error would require to undo unifications performed during the execution of the built-in: An implementor has thus two possibilities to implement this:

  • create a "choice point" at the time of invoking a built-in predicate, this would incur a lot of additional overhead, both for creating this choice point and for "trailing" subsequent bindings
  • go through each and every built-in predicate manually and decide on a case-by-case basis how to handle errors — while this is the most efficient in terms of runtime overheads, this is also the most costly and error-prone approach

Similar complexities are caused by exploiting WAM registers within built-ins. Again, one has the choice between a slow system or one with significant implementation overhead.

exception_handler/3

Many systems, however, provide internally better mechanisms, but few offer them consistently to the programmer. IF/Prolog provides exception_handler/3 which has the same arguments as catch/3 but handles the error or exception locally:

[user] ?- catch((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).

no

[user] ?- exception_handler((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).

Z       = ok

yes

setup_call_cleanup/3

This built-in offered by quite a few systems. It is very similar to unwind-protect but requires some additional complexity due to Prolog's backtracking mechanism. See its current definition.


All these mechanisms need to be provided by the system implementor, they cannot be built on top of ISO Prolog.

这篇关于Prolog 是否有条件并像 Common Lisp 那样重启系统?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆