prolog中的Zebra解决方案是如何工作的


zebra_owner(Owner) :-
houses(Hs),
member(h(Owner,zebra,_,_,_), Hs).
water_drinker(Drinker) :-
houses(Hs),
member(h(Drinker,_,_,water,_), Hs).

houses(Hs) :-
length(Hs, 5),                                            %  1
member(h(english,_,_,_,red), Hs),                         %  2
member(h(spanish,dog,_,_,_), Hs),                         %  3
member(h(_,_,_,coffee,green), Hs),                        %  4
member(h(ukrainian,_,_,tea,_), Hs),                       %  5
adjacent(h(_,_,_,_,green), h(_,_,_,_,white), Hs),         %  6
member(h(_,snake,winston,_,_), Hs),                       %  7
member(h(_,_,kool,_,yellow), Hs),                         %  8
Hs = [_,_,h(_,_,_,milk,_),_,_],                           %  9
Hs = [h(norwegian,_,_,_,_)|_],                            % 10
adjacent(h(_,fox,_,_,_), h(_,_,chesterfield,_,_), Hs),        % 11
adjacent(h(_,_,kool,_,_), h(_,horse,_,_,_), Hs),              % 12
member(h(_,_,lucky,juice,_), Hs),                         % 13
member(h(japanese,_,kent,_,_), Hs),                       % 14
adjacent(h(norwegian,_,_,_,_), h(_,_,_,_,blue), Hs),          % 15
member(h(_,_,_,water,_), Hs),       % one of them drinks water
member(h(_,zebra,_,_,_), Hs).       % one of them owns a zebra
adjacent(A, B, Ls) :- append(_, [A,B|_], Ls).
adjacent(A, B, Ls) :- append(_, [B,A|_], Ls).

如果列表实际上一直是空的,那么houses(Hs)谓词为什么不通过各种member(elem, list)检查?我知道这听起来可能是一个愚蠢的问题,但我很难理解Prolog,尤其是在多年的面向对象编程之后。感谢您的帮助!

编辑:我没有提到我问prolog的问题,就是这个zebra_owner(Owner)

编辑2:也张贴问题的文本(有点著名(供参考:

  1. 五种颜色的房子排成一排,每个房子都有一个主人、一只宠物、香烟和一杯饮料
  2. 英国人住在红房子里
  3. 西班牙人养了一只狗
  4. 他们在温室里喝咖啡
  5. 乌克兰人喝茶
  6. 绿色的房子在白色的房子旁边
  7. 温斯顿吸烟者有一条蛇
  8. 在黄色的房子里,他们抽库尔
  9. 在中间的房子里,他们喝牛奶
  10. 挪威人住在左边第一栋房子里
  11. 切斯特菲尔德的烟鬼住在带狐狸的男人附近
  12. 在房子附近的房子里,他们和马一起抽库尔
  13. 幸运一击吸烟者喝果汁
  14. 日本人抽烟
  15. 挪威人住在蓝色的房子附近

谁拥有斑马,谁喝水?

如果列表实际上一直是空的,Houses(Hs(谓词为什么不通过各种成员(elem,list(检查?

这是因为列表实际上并不是空的!Prolog中的空列表是[]。它不包含任何元素。

但是,对houses(Hs)的调用中的列表由该谓词中的第一个目标(即length(Hs, 5)(控制。这个目标意味着什么?

了解Prolog的一个重要方面是,目标或查询不仅仅意味着";这个说法是真的吗&";。Prolog目标或查询意味着">在什么情况下这句话是真的吗&";。Prolog将尝试向您描述您的查询成为真的情况。

因此,即使我们以前对Hs一无所知,在执行这个length查询时,GNU Prolog会说:

| ?- length(Hs, 5).
Hs = [_,_,_,_,_]

如果Hs的形式为[_, _, _, _, _],则length(Hs, 5)变为真。这不是一个空列表。它甚至不是一个可能为空的列表。这是一个明确包含五个元素的列表。我们对这些元素一无所知!但名单的长度是固定的。

Prolog允许你谈论一个肯定有元素但元素未知的列表,这可能会误导你。这是一个在典型的面向对象语言中不可能实现的概念。但是,我们不知道这些元素并不意味着那里什么都没有,也就是说,列表是空的。

至于member调用是如何成功的:同样,它们不仅仅是检查";CCD_ 13是CCD_;。这些问题的意思是">在什么情况下XHs的成员&";。再次,Prolog将为您做一些工作来描述这些情况:

| ?- length(Hs, 5), member(x, Hs).
Hs = [x,_,_,_,_] ? ;
Hs = [_,x,_,_,_] ? ;
Hs = [_,_,x,_,_] ? ;
Hs = [_,_,_,x,_] ? ;
Hs = [_,_,_,_,x]

因此,如果xHs的第一个元素,或者第二个元素,或第三个元素,等等,则xHs的成员;描述情况";意味着Prolog实际上将列表的适当元素(之前是变量(绑定到元素CCD_ 21。对于这些可能性中的每一种,member目标之后的进一步计算可以进一步实例化列表。这就是构建斑马之谜解决方案的原因:;是";对于问题";谜题有解决方案吗";,Prolog构建了一个实际描述解决方案的数据结构。

房屋列表Hs,永远不会是空的。它是在一开始就创建的

length(Hs, 5)

因此,它是一个包含五个最初未初始化的逻辑变量的列表,当它们被依次调用时,它们会被所有后续目标实例化,特别是被您提到的member/2目标实例化。

要查看此示例,请尝试:

32 ?- L=[A,B,C], member(1, L).
L = [1, B, C] ;
L = [A, 1, C] ;    % three solutions are found by Prolog
L = [A, B, 1].     % in turn, one after another

那些无法实现的调用(其中不可能实例化剩余的空闲逻辑变量(会导致特定的搜索分支失败。

一个例子:

33 ?- L=[1,2,C], member(3,L).    % a check succeeds, while 
L = [1, 2, 3], C = 3.            % instantiating the `C` logvar
34 ?- L=[1,2,3], member(3,L).    % a check succeeds
L = [1, 2, 3].
35 ?- L=[1,2,3], member(4,L).    % a check fails
false.

Hs中的所有逻辑变量都被完全实例化时,最终只剩下成功的实例化,从而形成解决方案。

最新更新