微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

是否应该通过引发实例化错误来强制执行模式声明?

如何解决是否应该通过引发实例化错误来强制执行模式声明?

我一直在研究某些谓词,它们在某些模式下使用时不会终止或给出错误解决方案。

这里是一个例子:

%!  list_without_duplicates(+List1,-List2) is det.
%
%   True if List2 contains all the elements of List1 but has
%   no duplicate elements. 
%
%   Ex: list_without_duplicates([1,1,2,3,3],[1,3]).

list_without_duplicates([],[]).
list_without_duplicates([X|Xs],[X|Acc]) :-
    \+ memberchk(X,Xs),list_without_duplicates(Xs,Acc).
list_without_duplicates([X|Xs],Acc) :-
    memberchk(X,Acc).

% This is great.
?- list_without_duplicates([1,X).
X = [1,3] ; 
false.

% This is not great.
list_without_duplicates_(X,3]).
ERROR: Stack limit (1.0Gb) exceeded
ERROR:   Stack sizes: local: 1Kb,global: 0.8Gb,trail: 0.1Mb
ERROR:   Stack depth: 16,586,last-call: 100%,Choice points: 5
...

所以我的问题是,如果没有实例化第一个参数,我是否最好抛出一个错误

list_without_duplicates(List1,List2) :-
    (  var(List1) 
    -> instantiation_error(List1)
    ;  list_without_duplicates_star(List1,List2)
    ).

list_without_duplicates_star([],[]).
list_without_duplicates_star([X|Xs],list_without_duplicates_star(Xs,Acc).
list_without_duplicates_star([X|Xs],Acc).

我一直在阅读一些Prolog库,例如apply.pl,该库在我的系统上位于/usr/local/logic/lib/swipl/library/apply.pl中。这是直接来自此库的代码。请注意,此处任何地方都没有提及实例化错误

maplist(Goal,List1,List2) :-
    maplist_(List1,List2,Goal).

maplist_([],[],_).
maplist_([Elem1|Tail1],[Elem2|Tail2],Goal) :-
    call(Goal,Elem1,Elem2),maplist_(Tail1,Tail2,Goal).

但是,如果我像这样使用此谓词,则会收到实例化错误

?- use_module(library(apply)).
true.

?- apply:maplist(X,[4,5,6]).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [11] apply:maplist_([1,2|...],5|...],apply:_5706)
ERROR:    [9] toplevel_call(user:apply: ...) at /usr/local/logic/lib/swipl/boot/toplevel.pl:1113
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

我不明白Prolog如何知道会抛出此错误

解决方法

除非您绝对确定没有其他选择,否则我不会直接输入您的Prolog代码。

使用内置功能,它们免费提供了许多“类型检查”。将call与不可调用一起使用是一个示例。基本上所有内置插件都会检查其参数,如果没有,我会认为这是一个错误并报告。例子:

?- between(1,3,foo).
?- succ(X,0).
?- X = [_|X],length(X,N).
?- X is 3 - a.
?- X is 3 - A.
?- sort([a,b|c],Sorted).

换句话说,只要您找到适合在自己的代码中使用的内置程序,就无需显式抛出。

如果要检查参数,请继续并使用library(error)

“无重复”谓词是多年生经典。您需要一个很好的理由,不要为此使用sort / 2。如果您确实使用了sort / 2,则会立即收到错误消息:

?- sort(X,Y).
ERROR: Arguments are not sufficiently instantiated

如果您决定自己编写程序,则不妨顺其自然,按照if_/3的建议使用@false。事实上,如果您仅查看@false资料中的链接,就可以在SO上找到一个不错的解决方案。

,

如果第一个参数未实例化,我最好抛出一个错误吗?

您的情况不多。实际上,您遇到的无法终止很烦人且浪费资源,但至少不是不正确的。我会更担心以下情况:

?- Y = b,list_without_duplicates([a,Y],[a,b]). 
   Y = b
;  false.                         % inefficiency                         
?-        list_without_duplicates([a,b]).
   false.                         % incompleteness

在存在约束的情况下情况甚至更糟。

作为一般的经验法则,每当您要根据实例识别时,请测试更实例化的模式。就您而言,请勿使用var/1进行测试,而应使用nonvar/1。这会将您的注意力集中在更安全的情况下。在您的情况下,您可能已经意识到仅靠nonvar/1是不够的。实际上,请使用ground/1

list_without_duplicates(List1,List2) :-
    (  ground(List1) 
    -> list_without_duplicates_star(List1,List2)
    ; instantiation_error(List1)
    ).

考虑使用iwhen/2隐藏详细信息;并轻松升级到相关程序:只需删除i,您就可以使用when/2

通常,此处的实例化错误掩盖了程序问题。其中一些与非终止有关,而其他一些则有助于掩盖诸如memberchk/2之类的不纯代码的非关系部分。

问题仍然存在,为什么首先要编写不纯代码?如果它像您一样效率低下,还更是如此?借助library(reif),您将获得一个干净,纯净和高效的解决方案:

:- use_module(library(reif)).
list_nub([],[]).
list_nub([X|Xs],Ys0) :-
   if_(memberd_t(X,Xs),Ys0 = Ys1,Ys0 = [X|Ys1]),list_nub(Xs,Ys1).

响应@gusbro对SWI性能的评论,这是SICStus Prolog的扩展(要获得该清单,我宣布list_nub/2为动态)。扩展在SWI中看起来应该相似。

list_nub([],[]).
list_nub([A|B],C) :-
        memberd_t(A,B,D),(   D==true ->
            C=E
        ;   D==false ->
            C=[A|E]
        ;   nonvar(D) ->
            throw(error(type_error(boolean,type_error(call(user:memberd_t(A,B),2,boolean,D)))
        ;   throw(error(instantiation_error,instantiation_error(call(user:memberd_t(A,2)))
        ),list_nub(B,E).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。