【问题标题】:SWI Prolog does not terminateSWI Prolog 不终止
【发布时间】:2012-11-20 15:30:10
【问题描述】:
:- 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) 会返回一个results 答案列表,但在多个results 答案后它会挂起。我不知道为什么 Prolog 不能返回 X 的所有可能值。

【问题讨论】:

  • 我猜它会忙着枚举 numYears 到无穷大...
  • 这是错字吗? is_differentfact(X) 我希望有两个参数。
  • @user1638891 否,maplist 向 is_differentfact(X) 添加了第二个参数,即 Xs 的元素。

标签: prolog clpfd prolog-dif failure-slice


【解决方案1】:

你问:

为什么?- is_fakt(L) ...但在多次结果 答案之后它挂起。

你说一个数字。该数字是按 SPACE 的 62 次以到达循环的那一刻。不是很长吗?而且你的程序很小。您将如何有机会在更大的计划中做同样的事情?别担心,有帮助。但是你需要换个角度看节目。

在 Prolog 中理解一个具体查询的非常精确的执行几乎是不可能的。你有两种不同类型的控制流交错加上奇怪的数据结构,它们不需要存在,但稍后“进来”;有时。所有这一切都开启了一个名副其实的可能执行痕迹,这些痕迹非常详细,以至于你的大脑会溢出——更糟糕的是:你的大脑仍然会假装你理解一切,但实际上是有效的你没有。并且错误在您的程序中有大量的聚会时间。这些虫子会在未来的某个时间点咬人,但只是在虫子咬人的基础上。这可能非常令人沮丧。毕竟,程序很小,应该很容易理解(按照命令式语言的标准)。但是,对于其他语言中非常复杂的问题,Prolog 程序往往非常紧凑。

尝试使用示踪剂逐步了解我的意思。你会看到各种各样的事情发生。而且大部分都是无关紧要的。

幸运的是,有很多方法可以理解 Prolog,但在这里您必须依赖语言本身的优良特性。对于未终止的本地化原因,最好开始考虑。您可以通过将目标 false 添加到程序中来从程序中获取失败切片。如果生成的程序仍然没有终止,那么我们有一个原因为什么我们的原始程序也没有终止。

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

:- 使用模块(库(clpfd))。 事实(处理= A):- 0..1 中的 A。 fact(numYears=B) :- B in 0..sup, falsefact(numDrugs=C) :- C in 0..sup, false事实(处理 2=D):- 0..1 中的 D,falsefact(cParam=E) :- E in 0..4, falseis_differentfact(X,X) :- 错误。 is_differentfact(元素=_,其他元素=_):- 差异(元素,其他元素)。 is_fakt([])。 is_fakt([X|Xs]) :- 事实(X), 地图列表(is_differentfact(X),Xs), is_fakt(Xs)。

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

?- is_fakt(Xs)。 Xs = [] ; Xs = [治疗=_G180099], _G180099 在 0..1 ; **循环**

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

你说:

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

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

((需要考虑的一些注意事项:您真的想以这种方式获得该列表吗?您将获得所有排列!使用长度为 n 的列表,您将获得 n! 不同的答案。对于 n = 10,即 3628800。这是你想要的吗?可能不是。))

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

为了更好地确定原因,让我们“关闭”所有答案。所以我们查询is_fakt(L),false 而不是:

:- 使用模块(库(clpfd))。 事实(处理= A):- 0..1 中的 A。 fact(numYears=B) :- B in 0..sup, falsefact(numDrugs=C) :- C in 0..sup, false事实(处理 2=D):- 0..1 中的 D,falsefact(cParam=E) :- E in 0..4, falseis_differentfact(X,X) :- 错误。 is_differentfact(元素=_,其他元素=_):- 差异(元素,其他元素)。 is_fakt([]) :- false。 is_fakt([X|Xs]) :- 事实(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], 差异(_G496,_G508); X = (_G552=_G553), Xs = [_G564=_G565, _G570=_G571], 差异(_G552,_G570), 差异(_G552,_G564); X = (_G608=_G609), Xs = [_G620=_G621, _G626=_G627, _G632=_G633], 差异(_G608,_G632), 差异(_G608,_G626), 差异(_G608,_G620); X = (_G664=_G665), Xs = [_G676=_G677, _G682=_G683, _G688=_G689, _G694=_G695], 差异(_G664,_G694), 差异(_G664,_G688), 差异(_G664,_G682), 差异(_G664,_G676); X = (_G720=_G721), Xs = [_G732=_G733, _G738=_G739, _G744=_G745, _G750=_G751, _G756=_G757], 差异(_G720,_G756), 差异(_G720,_G750), 差异(_G720,_G744), 差异(_G720,_G738), 差异(_G720,_G732)...

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

?- maplist(is_differentfact(X),Xs), false。 **循环**

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

更多信息,请查看标记为的其他解释

【讨论】:

  • 感谢您非常有见地的回答。我是否正确,我可以使用这个谓词来检查给定的事实列表是否是正确的事实列表(以任意顺序),但是当我想使用终止的谓词时,我必须找到一个更简单的解决方案只生成没有所有排列的列表?然后,我可以在使用该谓词检查给定列表之前对其进行排序。
  • @ri5b6:如果列表的长度固定,您的定义将终止。其他问题太投机了,但我看到你有自己的答案......
【解决方案2】:

基于 false 的 cmets 的编辑版本。

:- 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).

现在终止。

【讨论】:

  • 如果你在程序中使用findall/3 这样的结构,你会失去很多有用的属性。例如,您不能再使用故障切片。或者,如果你这样做了,你必须非常小心它们的使用。此外,将findall/3 与约束一起使用有点不安全:有时有效,有时无效,尤其是在某些变量被有效共享的情况下。几个系统已经多次改变了处理它们的方式。
  • 你可以使用select/3而不是member/2subtract/3,这也更常见。
  • 您当前的定义更简洁:is_fakt(X) :- facts(Facts), sort(Facts, X). 但我更建议使用keysort/2 并将(=)/2 替换为(-)/2。并且可能最好首先确定only 的名称列表。然后收集约束。
  • is_fakt 对于事实列表的每个成员的任意元素列表应该为真,这就是紧凑版本不起作用的原因。但是感谢您通过(-)/2 提供的提示。
  • 我会尝试为findall/3寻找替代方案
猜你喜欢
  • 2016-01-16
  • 2012-04-02
  • 1970-01-01
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多