SWI-prolog:如何通过使用一些事实制作一份循环清单

时间:2019-03-15 05:22:46

标签: list recursion prolog circular-list

loop1(a).
loop1(b).
loop1(c).
loop1(d).

circular(d, a).
circular(b, c).
circular(b, d).
circular(a, b).

所以我打电话给

in_cycle(a, Cycle). 

它将返回:

[a,b,d,a]

如果我打电话给

in_cycle(b, Cycle).

它将返回:

[b,d,a,b].

但是,如果您致电:

in_cycle(c, Cycle).

它将返回:

false. (because no loop is included).

这是我的尝试:

in_cycle(C,Cycle) :- circular(C,Cycle1),in_cycle(Cycle1,Cycle).

我知道此谓词有一个非常严重的问题:它不会停止...我真的想知道我应该添加哪种基本情况,以便该谓词停止?我应该添加一个条件,以便序言在找到相同的字母时将停止吗?

如果有人能帮助我,将不胜感激!

-------更新-------

check([Y,_|Z]) :- check([Y|Z]).

in_cycle(C, [C]).
in_cycle(C, [C, C1|Cycle]) :-  circular(C, C1),check([C,C1|Cycle]),
                               in_cycle(C1, [C1|Cycle]).

2 个答案:

答案 0 :(得分:1)

  1. 您的事实数据库中最短的周期是多少? circular(a, a).是周期[a]吗?知道最短的周期是什么,可以帮助您找到谓词的完成条件之一。
  2. 要能够找到列表,您的谓词需要构建它。您的in_cycle/2从不提及任何列表。您需要在其中的某个地方使用[Head | Tail]构造,才能将元素添加到列表中。
  3. 您已经对周期性列表的外观有所了解。第一个和最后一个元素相同,并且与您要为其查找循环的符号相同。使用该信息。
  4. 要知道何时完成一个循环,您需要记住您从哪个符号开始。为了能够通过递归做到这一点,您需要保持状态。因此,您将需要一个附加谓词来保存该状态。例如。涉及诸如in_cycle(Current_symbol, Cycle, Start_symbol)之类的谓词的事物。然后,您可以从in_cycle/2拨打电话。

让我们看看您的尝试:

in_cycle(C, Cycle) :-
    circular(C, Cycle1),
    in_cycle(Cycle1, Cycle).

您可以在提示符下使用the trace command来查看正在发生的事情:trace, in_cycle(a, X).

Space 可以单步执行程序。按 h 寻求帮助,然后按 a 退出。使用notrace.再次退出跟踪模式。

逐步浏览时,您会发现谓词很好地遍历了整个循环,但是X从来没有成为列表。不好。

让我们尝试建立一个清单。正如我在第(3)点中提到的那样,您已经对列表有所了解。列表的第一个元素与in_cycle的第一个参数相同。此外,列表的 second 元素与您在circular/2中可以找到的元素相同。因此,我们知道一个循环至少包含两个元素。怎么样?

in_cycle(First, [First, Next|Cycle]) :-
    circular(First, Next),
    in_cycle(Next, [Next|Cycle]).

如果立即跟踪此内容,您会发现X正在发生某些事情,但实际上并没有任何用处。 Cycle仍然是个谜,我们只是在不断地了解事实。您在这里需要一些结束条件。让我们尝试一个简单的例子:

in_cycle(First, [First]).
in_cycle(First, [First, Next|Cycle]) :-
    circular(First, Next),
    in_cycle(Next, [Next|Cycle]).

哇! in_cycle(a, X)突然产生结果!似乎所有以circular开头的a连接都使用的列表。那不是我们想要的,但是也许我们离我们越来越近了?

一个问题是in_cycle(Next, [Next|Cycle])实际上不正确!

如果您执行in_cycle(a, X),就已经知道X应该成为[a, b, d, a],因此将这些值填充到in_cycle(First, [First, Next|Cycle])中,您将得到:

First = a
Next = b
Cycle = [d, a]

到达in_cycle(Next, [Next|Cycle])时,表示为in_cycle(b, [b, d, a])。但是[b, d, a]不是一个循环!您需要能够以某种方式区分这两种情况。一种实现方法是调用一个单独的谓词,如我在(4)中提到的那样,以跟踪起始元素是什么。

答案 1 :(得分:1)

如果您可以找到返回该节点的路径,则该节点处于循环中。使用path/4

in_cycle(C, [C|Cs]) :-
   circular(C, A),
   path(circular, Cs, A,C).

现在,该谓词是否终止?我们如何系统地测试呢?我们如何确保我们不会忘记任何特殊情况?对于像这样的纯单调程序,终止测试是很简单的 1 :只需执行最通用的查询!那就是:

?- in_cycle(C, Cs).
   C = d,
   Cs = "dabd"           % [d,a,b,d]
;  C = b,
   Cs = "bdab"
;  C = a, Cs = "abda"
;  false.                % it terminates!

(请参见this answer,如何获得"bdab"代替[b,d,a,b])。

Prolog的优点是上述查询构成了终止证明。您可以构成的每个查询都包含在上述查询中。并且由于更一般的查询已经终止,因此任何更具体的查询也将终止!任何!

所有这些都适用于circular/2的任何变量自由事实。但是,这种证明不能像对一组特定事实的证明那样容易地进行。


1请注意,琐碎意味着属于trivium