我还在学习Prolog,但是我遇到了一小段代码,如果我理解正确的话,我不太清楚。
代码:
% Takes the spiders friends and returns a list with persons who don't know each other.
getConspirators([], Res, Res).
getConspirators([H|T], CConspirators, Res):-
append([H|T], CConspirators, PK),
knowsAtleastOne(PK),
% Gets all the friends of the possible conspirator H.
allFriends(H, PFriends),
subtract(T, PFriends, Pprim),
getConspirators(Pprim, [H|CConspirators], Res).
getConspirators([_|T], CConspirators, Res) :-
getConspirators(T, CConspirators, Res).
% Checks if any person Y or Y's friends know anybody in PK.
knowsAtleastOne(PK):-
forall(person(Y), (memberchk(Y,PK);friendCons(Y,PK))).
% Checks if a person X's friends know any of the conspirators.
friendCons(X,Conspirators):-
friend(X,Y),
memberchk(Y,Conspirators),
!.
(这不是整个程序,只是它的一小部分)
我不确定我是否理解了getConspirators([H|T], CConspirators, Res) :-
和getConspirators([_|T], CConspirators, Res) :-
部分
getConspirators
谓词。它们看起来几乎一样!现在,我知道“_”符号意味着“字面意义上的任何价值”(AKA Prolog并不关心它是什么价值)。但是Prolog如何知道在运行代码时要选择哪种情况?我的理论是Prolog运行getConspirators([_|T], CConspirators, Res) :-
情况,当且仅当getConspirators([H|T], CConspirators, Res) :-
情况在某个地方失败(返回false)时。我理解正确吗?
答案 0 :(得分:4)
这里有三个元素:回溯,统一和列表符号。我将用一个更简单的例子来解释这三个:
moon(europa).
moon(ganymede).
planet(jupiter).
planet(saturn).
我们知道木卫二和木卫三是木星(木星),木星和土星都是行星。当我们查询已知的行星时,我们写道:
?- planet(X).
X = jupiter ; % type ; for the next answer
X = saturn. % there's no more answer, hence .
当prolog查找适合于相应替换变量的查询的规则头时,就会发生统一。例如,没有替代使moon(X) = planet(Y)
相等,但planet(jupiter) = planet(X)
有一个,即X=jupiter
。这就是你如何获得第一个解决方案。对于第二种解决方案,Prolog需要与第二个规则头统一,即planet(saturn) = planet(X)
。因为这是在完全枚举第一个选项之后完成的,所以我们称之为回溯。
现在我们可以专注于(链接)列表。列表为空([]
)或者在尾部列表X
(Xs
)前面添加了第一个元素[X|Xs]
。 Prolog对列表[X | [Y | [] ]]
也有一个更好的表示法,即[X,Y]
。在内部它们是相同的。当我们现在想要收集星体对象列表时,我们可以制定以下三个规则:
astral_objects([]). % The empty list is a list of astral objects.
astral_objects([X|Xs]) :- % The list [X | Xs] is a list of astral objects if...
moon(X), % ... its first element X is a moon
astral_objects(Xs). % ... and the remaining list Xs is a list of astral objects
astral_object([X|Xs]) :- % Likewise for planets
planet(X),
astral_objects(Xs).
当我们为两元素列表制定查询时,我们得到所有对象组合:
?- astral_object([A,B]).
A = B, B = europa ;
A = europa,
B = ganymede ;
A = europa,
B = jupiter ;
A = europa,
B = saturn ;
A = ganymede,
B = europa ;
A = B, B = ganymede ;
A = ganymede,
B = jupiter
%...
统一时,只适用规则2和3。在这两种情况下,我们都有astral_objects([X|Xs]) = astral_objects([A,B])
。请注意,[A,B]
是[A|[B]]
的简写,X=A
和Xs=[B]
的简写。身体的第一条规则将X
与相应的月球/行星统一,递归步骤描述尾部。我们再次统一astral_objects([X|Xs]) = astral_objects([B])
,导致X=B
和Xs = []
。现在,递归步骤只匹配空列表的终端案例,我们已经完全探索了这条路径。
现在如果我们寻找任意星体对象列表会发生什么?
?- astral_object(Xs).
Xs = [] ;
Xs = [europa] ;
Xs = [europa, europa] ;
Xs = [europa, europa, europa] ;
Xs = [europa, europa, europa, europa] ;
Xs = [europa, europa, europa, europa, europa]
%... does not terminate
头部astral_objects(Xs)
匹配所有三个身体。在返回终端案例的替换后,它一遍又一遍地下降到第一个规则。由于列表的长度不受限制,因此在尝试第三条规则之前,可以找到无数的解决方案。为避免这种情况,您可以在尝试使列表满足谓词之前公平地枚举列表:
?- length(Xs,_), astral_object(Xs).
Xs = [] ;
Xs = [europa] ;
Xs = [ganymede] ;
Xs = [jupiter] ;
Xs = [saturn] ;
Xs = [europa, europa] ;
Xs = [europa, ganymede] ;
Xs = [europa, jupiter] ;
Xs = [europa, saturn] ;
Xs = [ganymede, europa]
%...
它仍然没有终止,但是你会看到列表的上升长度,因此也就是变化。
答案 1 :(得分:3)
问的问题是“ getConspirators([H|T], CConspirators, Res) :- _body_
和getConspirators([_|T], CConspirators, Res) :- _body_
部分……我的理论是Prolog运行getConspirators([_ | T],CConspirators,Res):-仅当且仅当情况发生时如果getConspirators([H | T],CConspirators,Res):-大小写失败(返回false)”
您的理论不正确。他们都将匹配。唯一的区别是,对于getConspirators([H|T], CConspirators, Res) :- _body_
而言,列表的第一个元素将在 body 中作为名为H
的变量使用。但是对于getConspirators([_|T], CConspirators, Res) :- _body_
,列表的第一个元素将在 body 中作为命名变量不可用。
如本代码所示,一种解释_
含义的好方法是“一个我以后不愿引用的变量”。