以下是我的知识库:
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
做出了两个不同的选择,但不报告它们,而只报告其他选择,分别为X
和M
,这两种选择都是相同的,所以看起来像重复。
你的问题不在于你认为它在哪里。首先,让我向您展示如何从命令行运行程序。这是一个"你好世界":
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,