假设你有一群下棋的人。你想确定一个玩家是否存在从不输给任何人(不败)。假设事实是以赢得的(詹姆斯,汤姆)给出的。赢了(詹姆斯,彼得),赢了(克雷格,汤姆)。失去了(皮特,汤姆)。然后玩家X根据条件从未输过。
undefeated(X) :- \+ won(_, X), \+ lost(X, _).
如此不败(詹姆斯)是真实的,不败(克雷格)是真实的但不败(汤姆)和不败(彼得)是假的。现在问题在调用不败(X)时出现,因为这将只返回否。要解决这个问题,我们需要一个在开始时始终为true的语句,因此我们为分别播放白色和黑色片段的播放器添加事实whiteplayer()和blackplayer()。
whiteplayer(james).
whiteplayer(craig).
whiteplayer(tom).
whiteplayer(peter).
blackplayer(james).
blackplayer(peter).
blackplayer(tom).
并将不败变为:
undefeated(X) :- (whiteplayer(X); blackplayer(X)), \+ won(_, X), \+ lost(X, _).
但现在我们遇到了一个新问题。查询不败(X)给出了解决方案詹姆斯,克雷格,詹姆斯,这发生了,因为詹姆斯同时扮演白色和黑色棋子,但克雷格只扮演白色棋子。所以问题是不败(詹姆斯)继续为白人(詹姆斯)和黑人(詹姆斯)继续,我怎么能做到这样只有在回溯时才进行其中一个?
答案 0 :(得分:3)
如果您的事实和规则允许变量的特定实例化以多种方式成功,那么除非您使用剪切(这通常也会消除有效的解决方案,这是不可取的),否则您将获得重复,或者您可以收集使用setof
(或“手动”)的冗余子解决方案可以消除重复。
所以你可以这样做:
validplayer(X) :- whiteplayer(X) ; blackplayer(X).
undefeated(Player) :-
setof(P, validplayer(P), Players),
member(Player, Players),
\+ won(_, Player), \+ lost(Player, _).
理想情况下,如果您可以定义validplayer/1
以使其对任何给定的玩家成功一次并且不切割,则会更好。但是你目前对事实的定义并没有实现这一点。
我还建议您不要同时使用won/2
和lost/2
个事实,因为won(X, Y)
相当于lost(Y, X)
。最好坚持使用其中一个。希望您的数据库不同时包含won(john, paul)
和lost(paul, john)
,否则上述解决方案仍会产生重复项,您需要setof/3
上的undefeated/1
:{{ 1}}。
答案 1 :(得分:0)
如果你知道至少有一个人从X
获胜,那么X
并不是不败的。你只需尝试找到至少一个Z
,Z
从X
获胜。如果发生这种情况,则意味着prolog找到一个匹配,它会尝试返回true而你只是反转这个值是假的并且削减了所有其他分支。
对于没有不败的人也是如此。在这种情况下,prolog找不到匹配并返回false而不反转它的值。
这是你的代码,如果我理解你的问题,那么使用guitracer看看发生了什么总是好的。
won(james,tom).
won(james,peter).
won(craig,tom).
lost(peter,tom).
undefeated(X):-
\+ won(_Z,X),!.