繁体   English   中英

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

[英]SWI-prolog : how to make a circular list by using some facts

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]).
  1. 您的事实数据库中最短的周期是多少? 会是circular(a, a). 被循环[a] 知道最短的周期是什么,可以帮助您找到谓词的完成条件(其中之一)。
  2. 为了能够找到列表,您的谓词需要构建它。 您的in_cycle/2永远不会提及任何列表。 您需要使用[Head | Tail] [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).

您可以在提示符处使用trace命令查看正在发生的情况: trace, in_cycle(a, X).

空格键单步执行程序。 h寻求帮助,按a退出。 使用notrace. 再次退出跟踪模式。

在逐步执行此过程时,您会发现谓词很好地循环了整个循环,但是X永远不会成为列表。 那很糟。

让我们尝试建立一个清单。 正如我在第(3)点中提到的那样,您已经对列表有所了解。 列表的第一个元素与in_cycle的第一个参数in_cycle 更重要的是,列表的第二个元素与您在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)突然给出结果! 似乎所有使用以a开头的circular连接的列表。 那不是我们想要的,但是也许我们正在接近?

一个问题是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)中提到的那样,以跟踪您的起始元素是什么。

如果您可以找到返回该节点的路径,则该节点处于循环中。 使用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!

(请参阅此答案,如何获得"bdab"代替[b,d,a,b] )。

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

所有这一切,甚至对于circular/2任何可变自由事实都成立。 但是,这种证明不能像针对特定事实的证明那样容易地进行。


1请注意, 琐碎的意思是属于琐事

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM