Don';t在Prolog中重复求解
假设您有一个包含以下内容的数据库:Don';t在Prolog中重复求解,prolog,prolog-setof,Prolog,Prolog Setof,假设您有一个包含以下内容的数据库: son(a, d). son(b, d). son(a, c). son(b, c). 所以a和b是d和c的儿子。现在你想知道,如果有一个更大的数据库,谁是谁的兄弟。解决办法是: brother(X, Y) :- son(X, P), son(Y, P), X \= Y. 问题是如果你问“brother(X,Y)”,然后开始按“;“你会得到多余的结果,比如: X=a,Y=b X=b,Y=a X=a,Y=b X=b,Y=a 我可以
son(a, d).
son(b, d).
son(a, c).
son(b, c).
所以a和b是d和c的儿子。现在你想知道,如果有一个更大的数据库,谁是谁的兄弟。解决办法是:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
问题是如果你问“brother(X,Y)”,然后开始按“;“你会得到多余的结果,比如:
- X=a,Y=b李>
- X=b,Y=a李>
- X=a,Y=b李>
- X=b,Y=a李>
我可以理解为什么我会得到这些结果,但我正在寻找一种方法来解决这个问题。我能做什么?您可以通过比较消除一组:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y, X @< Y.
?- brother(X, Y).
X = a,
Y = b ;
X = a,
Y = b ;
false.
这种方法的缺点是,您最终得到的是一个列表,而不是Prolog生成不同的解决方案,尽管您可以使用
member/2
来恢复这种行为。Prolog将始终尝试根据您的事实集为您的陈述找到所有可能的解决方案。扩展作为深度优先搜索工作:
son(a, d).
son(b, d).
son(a, c).
son(b, c).
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
brother(X, Y)
_______________________|____________________________ [son(X, P)]
| | | |
X = a, P = d X = b, P = d X = a, P = c X = a, P = b
| | | |
| ... ... ...
|
| (X and P are already defined for this branch;
| the algorithm now looks for Y's)
|__________________________________________ [son(Y, d)]
| |
son(a, d) -> Y = a son(b, d) -> Y = b
| |
| | [X \= Y]
X = a, Y = a -> false X = a, Y = b -> true
|
|
solution(X = a, Y = b, P = d)
但是,正如您所看到的,扩展将在所有分支中执行,因此最终您将得到更多与最终答案相同的解决方案。正如@Daniel Lyons所指出的,您可以使用内置的集合
您也可以使用代码>--cut运算符--一旦发现分支有效,则停止“水平”扩展,或者添加一些避免多个解决方案的语句
有关更多信息,请查看算法。这应该是可行的。但我认为它可以改进(我不是Prolog专家):
兄弟(X,Y):-
儿子(X,P1),
儿子(Y,P1),
X,Y,,
(子(X,P2),子(Y,P2),P1@假;真)。
如果您使用的是草莓Prolog编译器,键入以下内容将无法获得所有答案:
?- brother(X, Y),
write(X), nl,
write(Y), nl.
为了得到所有答案,写下以下内容:
?- brother(X, Y),
write(X), nl,
write(Y), nl,
fail.
我希望它能帮助你。:)我得到了一个答案
%包括字典
:-[p1]。%《儿子的字典》
:-动态(已找到/2)。
兄弟(X,Y):-
%从数据库中找到两个人进行测试
儿子(X,P),
儿子(Y,P),
%测试两个人是否不同,是否还没有使用过
testBrother(X,Y)。
%如果它到了这里,那是因为上面没有其他人可以测试,所以只需失败并收回所有数据
兄弟(u,u):-
收回(找到(,)),
失败。
testBrother(X,Y):-
X\=Y,
\+发现(X,Y),
\+发现(Y,X),
%如果没有使用它们,则会成功并断言所发现的内容
断言(找到(X,Y))。
它总是在最后失败时返回,但在以下情况下成功
- 兄弟(X,Y)。%每个兄弟都不重复
- 兄弟('Urraca',X)。%乌拉卡的每一个兄弟都不重复
- 兄弟('Urraca','Sancho I')。%没错,因为乌拉卡和桑乔我有着同样的父亲和母亲。事实上,即使他们只有相同的母亲或父亲,这也会变成现实。有点脱离上下文,但仍然有效,如果他们有三个或三个以上的共同父母,这仍然有效
它在以下情况下失败:
- 兄弟(X,X)。%错,因为是同一个人
- 兄弟(‘不’,X)。%False,因为数据库中甚至没有
- 兄弟(‘不’、‘桑乔一世’)。%错误,同样的原因
例如,我可以这样问:兄弟(X,Y),然后开始按“;”键,不重复地看到每个兄弟姐妹
假设a和b是数据库中的人,我也可以做brother(a,b)和brother(b,a)。这一点很重要,因为有些解决方案会使用@<来测试事物,因此兄弟(b,a)会失败
就是这样。首先,我建议不要动态更新Prolog数据库。出于某些原因,考虑这篇文章
您可以使用内置的集合/3
和成员/2
,正如@DanielLyons在他的回答中所建议的那样
作为另一种选择,考虑下面的查询,它使用了<代码> SETOF/3 以一种非常不寻常的方式,例如:
?- setof(t,brother(X,Y),_).
X = a, Y = b ;
X = b, Y = a.
我真的明白这一点。我试图找到解决问题的方法,我做到了。谢谢你详尽的回答。这可以解决重复解决方案的问题,但会引发另一个问题。询问“:-brother('a','b')”返回true,但询问“brother('b','a')”返回false。但是谢谢你的回答。这是一个很好且简单的技巧,确实解决了我想要的部分问题。这是真的,但如果希望兄弟(b,a)
为真,而不是由兄弟(X,Y)
生成,那将是非常糟糕的Prolog风格。总的来说,你不会不想产生可接受的解决方案,就像我对@Daniel Lyons说的那样。这也是一个很好的解决问题的办法,但它提出了另一个问题。询问“:-brother('a','b')”返回true,但询问“brother('b','a')”返回false。谢谢你的回答,我不知道你最后一行的符号。实际上我正在使用swipl,无法测试你的解决方案。但是,对于许多可用的平台,有答案总是很好的!这是可行的,但我不想在自己的代码中这样做。我认为动态存储是一种最后的手段,一种绕过通常尝试/绑定/失败/解除绑定的统一Prolog想要做的方法。拥有在看似纯粹逻辑谓词的过程中神奇地出现和消失的动态状态是很多机器和很多隐藏bug的地方。如果我在代码库中看到这一点,我会担心这样的事情可能会到处发生,使软件难以隔离和调试。我明白了。谢谢你的反馈(在这里和其他评论中),我确实同意你所说的,但是我真的需要这个,就像我展示的那样。添加一个链接到为什么结果会重复会对读者有帮助。我喜欢这个解决方案。我认为这个问题可以用bagof
而不是setof来解决。我错了吗?
?- brother(X, Y),
write(X), nl,
write(Y), nl.
?- brother(X, Y),
write(X), nl,
write(Y), nl,
fail.
?- setof(t,brother(X,Y),_).
X = a, Y = b ;
X = b, Y = a.