Prolog - 如何[H | _]和[_ | T]工作?

时间:2017-12-03 12:56:09

标签: recursion prolog clause

我还在学习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)时。我理解正确吗?

2 个答案:

答案 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)。因为这是在完全枚举第一个选项之后完成的,所以我们称之为回溯。

现在我们可以专注于(链接)列表。列表为空([])或者在尾部列表XXs)前面添加了第一个元素[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=AXs=[B]的简写。身体的第一条规则将X与相应的月球/行星统一,递归步骤描述尾部。我们再次统一astral_objects([X|Xs]) = astral_objects([B]),导致X=BXs = []。现在,递归步骤只匹配空列表的终端案例,我们已经完全探索了这条路径。

现在如果我们寻找任意星体对象列表会发生什么?

?- 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 中作为命名变量不可用。 如本代码所示,一种解释_含义的好方法是“一个我以后不愿引用的变量”。