SWI Prolog不会终止

时间:2012-11-20 15:30:11

标签: prolog clpfd prolog-dif failure-slice

:- use_module(library(clpfd)).

fact(treated=A) :- A in 0..1.
fact(numYears=B) :- B in 0..sup.
fact(numDrugs=C) :- C in 0..sup.
fact(treated2=D) :- D in 0..1.
fact(cParam=E) :- E in 0..4.

is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
    dif(Element,OtherElement).

is_fakt([]).
is_fakt([X|Xs]) :-
    fact(X),
    maplist(is_differentfact(X),Xs),
    is_fakt(Xs).

为什么?- is_fakt(X)会返回结果答案的列表,但在多个结果答案之后它会挂起。我不知道为什么Prolog无法返回X的所有可能值。

2 个答案:

答案 0 :(得分:5)

你问:

  

为什么?- is_fakt(L) ...但是在一些结果 答案之后它会挂起。

你说一个数字。这个数字是62次按 SPACE 来达到循环的那一刻。好久不是吗?你的程序很小。您将如何有机会通过更大的计划做同样的事情?别担心,有帮助。但是你需要从不同的角度来看待这个程序。

在Prolog中,理解非常精确地执行具体查询几乎是不可能的。你有两种不同类型的控制流交错加上奇怪的数据结构,不需要存在,但后来“进来”;有时。所有这些都打开了一系列真实的可能的执行痕迹,这些痕迹充满了细节,你的思想会溢出 - 更糟糕的是:你的思想仍会假装你理解所有事情,但却有效你没有。并且错误在你的程序中有很大的聚会时间。这些错误将在未来的某个时间点出现,但仅限于基于错误的基础。这可能非常令人沮丧。毕竟,该程序非常小,应该易于理解(按照命令式语言的标准)。但是,对于其他语言中非常复杂的问题,Prolog程序往往非常紧凑。

尝试使用跟踪器来查看我的意思。你会看到各种各样的事情发生。而且大多数都是无关紧要的。

幸运的是,有很多方法可以理解Prolog,但在这里你必须依赖语言本身的优秀属性。对于本地化非终止的原因,最好是开始考虑。您可以通过在程序中添加 false 目标,从程序中获取失败片段。如果结果程序仍然没有终止,我们就有理由说明原始程序也不会终止。

想一想:我们不是试图理解你的程序,而是做一些人类更擅长的事情:做出有根据的猜测。这个猜测可能会出错,但我们可以轻松检查。一开始你会猜测得非常糟糕。很快你会发现你可以系统地做很多事情。现在变得无关的所有代码都是 stike through

:- use_module(library(clpfd)).

fact(treated=A) :- A in 0..1.     
fact(numYears=B) :- B in 0..sup, false.
fact(numDrugs=C) :- C in 0..sup, false.
fact(treated2=D) :- D in 0..1, false.
fact(cParam=E) :- E in 0..4, false.

is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
    dif(Element,OtherElement).

is_fakt([]).
is_fakt([X|Xs]) :-
    fact(X),
    maplist(is_differentfact(X),Xs),
    is_fakt(Xs).

我们获得了什么?我们可以更快地缩小问题的范围:

?- is_fakt(Xs).
Xs = [] ;
Xs = [treated=_G180099],
_G180099 in 0..1 ;
**loops**

在继续之前,我尝试了解您对is_fakt/1的意思。你可能的意思是:所有事实都按照他们的名字,并确保没有重复。现在我们只有名为treated的事实,所以我们只能生成一个长度为1的列表。然后它循环。

你说:

  

我不知道为什么Prolog无法返回X的所有可能值。

要挑剔,那不是真的。 Prolog确实列举了X的所有可能值。但它没有终止。

((需要考虑的一些评论:你真的想以这种方式获得该列表吗?你将获得所有排列!使用长度 n 的列表,你将获得 n!< / em>不同的答案。对于 n = 10 即3628800.这是你想要的吗?可能不是。))

但是,让我们首先坚持确定不终止的确切原因。

为了更好地确定原因,让“关闭”所有答案。因此,我们使用以下代码查询is_fakt(L), false

:- use_module(library(clpfd)).

fact(treated=A) :- A in 0..1.     
fact(numYears=B) :- B in 0..sup, false.
fact(numDrugs=C) :- C in 0..sup, false.
fact(treated2=D) :- D in 0..1, false.
fact(cParam=E) :- E in 0..4, false.

is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
    dif(Element,OtherElement).

is_fakt([]) :- false.
is_fakt([X|Xs]) :-
    fact(X),
    maplist(is_differentfact(X),Xs), false,
    is_fakt(Xs).

这是一个最小的故障片。所以它是maplist/2并不首先终止。您的想法是确保X的事实名称与Xs中的事实名称不同。但是如果Xs没有绑定,那将永远不会终止。我们来试试吧:

?- maplist(is_differentfact(X),Xs).
Xs = [] ;
X = (_G496=_G497),
Xs = [_G508=_G509],
dif(_G496, _G508) ;
X = (_G552=_G553),
Xs = [_G564=_G565, _G570=_G571],
dif(_G552, _G570),
dif(_G552, _G564) ;
X = (_G608=_G609),
Xs = [_G620=_G621, _G626=_G627, _G632=_G633],
dif(_G608, _G632),
dif(_G608, _G626),
dif(_G608, _G620) ;
X = (_G664=_G665),
Xs = [_G676=_G677, _G682=_G683, _G688=_G689, _G694=_G695],
dif(_G664, _G694),
dif(_G664, _G688),
dif(_G664, _G682),
dif(_G664, _G676) ;
X = (_G720=_G721),
Xs = [_G732=_G733, _G738=_G739, _G744=_G745, _G750=_G751, _G756=_G757],
dif(_G720, _G756),
dif(_G720, _G750),
dif(_G720, _G744),
dif(_G720, _G738),
dif(_G720, _G732) ...

看起来不太好......但我们可以做得更好:

?- maplist(is_differentfact(X),Xs), false.
**loops**

所以它循环。这是不终止的原因。为了解决这个问题,我们必须在故障片的剩余可见部分做一些事情......

有关详情,请查找标记为

的其他说明

答案 1 :(得分:0)

基于false的评论编辑版本。

:- use_module(library(clpfd)).
:- use_module(library(lists)).

fact(treated-X) :- X in 0..1.
fact(numYears-X) :- X in 0..sup.
fact(numDrugs-X) :- X in 0..sup.
fact(treated2-X) :- X in 0..1.
fact(cParam-X) :- X in 0..4.

facts(Facts) :-
findall(X,fact(X),Facts).

is_fact2(_, []).
is_fact2(Facts, [X|Xs]) :-
    member(X,Facts),
    select(X,Facts,Remaining),
    is_fact2(Remaining,Xs).

is_fakt(X) :-
    facts(Facts),
    is_fact2(Facts,X),
    keysort(X,X).

现在终止。