为什么Prolog在打印指令数量超过一个时复制所有答案?



以下是我的知识库:

parent(claudia, taiane).
parent(wl, cris).
parent(cris, jim).
parent(cris, pedro).
man(jim).
man(pedro).
man(w).
man(wl).
woman(taiane).
woman(claudia).
woman(cris).

当我的主要只有一个print时:

main:-
man(M),
print(M), nl, 
fail.
main:- halt.

当我用swipl -q -s exercise-family-tree.pl -g main执行它时,我得到了不重复的结果(都很好)。 另一方面,当我在我的主查询中查询更多并且print两次使用两个不同的变量作为我的main参数时:

main:-
%which women have both a father and a son in the database?
woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl, 
man(M),
print(M), nl, 
fail.
main:- halt.

而不是结果被重复。为什么我的代码在第二种情况下重复所有答案?

这个问题与我之前提出的问题不同。请让任何涉及 REPL 的交互式解决方案不在此问题的范围之内。

比结果重复。为什么我的代码在第二种情况下重复所有答案?

我认为您的问题与顶级或从命令行运行它的方式无关,而是您并不真正了解 Prolog 搜索、选择点和回溯,并且编写了一个嵌套循环,该循环打印相同的结果两次。

第一个代码的行为类似于(伪代码):

for M in [jim, pedro, w, wl]:
print(M)

第二个代码的作用类似于嵌套循环:

for _Child in [jim, pedro]:
print(chris)          % NB: same 'chris' 
%   for both choices of _Child
for M in [jim, pedro, w, wl]:
print(M)

更详细地说,带有man(M), print(M), nl, fail的第一个代码运行如下:

man(jim),   print(jim),   fail,  % backtrack to man(M)
man(pedro), print(pedro), fail,  % backtrack to man(M)
man(w),     print(w),     fail,  % backtrack to man(M)
man(wl),    print(wl),    fail.  % backtrack to man(M)
% no more man(M) choices

第二种情况,此代码:

woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl, 
man(M),
print(M), nl, 
fail.

运行方式如下:

woman(taiane),  parent(taiane,  ???)  % implicit fail
woman(claudia), parent(claudia, ???)  % implicit fail
woman(chris),parent(chris, jim),parent(wl, chris),man(wl),man(jim),print(chris)
%                          ~~~
% found a solution, now go forward through the man(M) code:
man(jim),   print(jim),   fail,  % backtrack to man(M)
man(pedro), print(pedro), fail,  % backtrack to man(M)
man(w),     print(w),     fail,  % backtrack to man(M)
man(wl),    print(wl),    fail,  % backtrack to man(M)
% no more man(M) choices, now the last choicepoint was 
% at parent(chris, S) which had choice S=jim or S=pedro.
% Redo that:
woman(chris),parent(chris, pedro),parent(wl, chris),man(wl),man(jim),print(chris)
%                          ~~~~~
% found a solution, now go forward through the man(M) code:
man(jim),   print(jim),   fail,  % backtrack to man(M)
man(pedro), print(pedro), fail,  % backtrack to man(M)
man(w),     print(w),     fail,  % backtrack to man(M)
man(wl),    print(wl),    fail.  % backtrack to man(M)
% no more man(M) choices, now parent(chris, S) has
% no more choices, so end.

因此,您从X=chris, parent(X, S)[jim,pedro]中为S做出了两个不同的选择,但不报告它们,而只报告其他选择,分别为XM,这两种选择都是相同的,所以看起来像重复。

你的问题不在于你认为它在哪里。首先,让我向您展示如何从命令行运行程序。这是一个"你好世界":

hello.pl内容 :

main :-
format("hello~n").

从命令行运行它而不进入顶层的方法:

$ swipl -g main -t halt hello.pl
hello

现在,这是一个在交互式顶层将回溯并具有多种解决方案的程序。只是为了演示,这是程序,在一个文件print.pl

main :-
between(1, 3, X),
format("~w~n", [X]).

如果您从顶层执行此操作,您将获得:

?- [print].
true.
?- main.
1
true ;
2
true ;
3
true.

好。但是,如果我们将其作为上面的"hello world"来做:

$ swipl -g main -t halt print.pl
1

现在,我将像您一样添加一个"失败"。这是我在文件printfail.pl中的新程序:

main :-
between(1, 3, X),
format("~w~n", [X]),
fail.

当我从命令行运行它时:

swipl -g main -t halt printfail.pl
1
2
3
ERROR: -g main: false

底线:通过非交互式运行程序来丢弃解决方案会让你失望。


还有一个脚注:即使找到解决方案,fail也会导致程序回溯。回溯撤消绑定,但无法撤消副作用。

尝试使用main作为:

main:-
%which women have both a father and a son in the database?
%trace,
woman(X), parent(X, S), parent(F, X), man(F), man(S), print(X), nl,
%man(M),
%print(M), nl,
fail.
main:- halt.

这显示了重复项:

$ swipl -q -s parent3.pl -g main
cris
cris

可以使用 https://www.swi-prolog.org/pldoc/doc_for?object=distinct/2 删除重复项,即:

distinct(X, (woman(X), parent(X, S), parent(F, X), man(F), man(S))), print(X), nl,

最新更新