为啥我无法通过 Prolog 获得 Ship Puzzle 的答案?

Posted

技术标签:

【中文标题】为啥我无法通过 Prolog 获得 Ship Puzzle 的答案?【英文标题】:Why I can't get an answer for the Ship Puzzle with Prolog?为什么我无法通过 Prolog 获得 Ship Puzzle 的答案? 【发布时间】:2015-08-24 17:36:26 【问题描述】:

我需要使用 Prolog 解决 Ship Puzzle 问题。 以下是事实。

有 5 艘船。

    希腊船六点出发,载着咖啡。 中间的船有一个黑色的烟囱。 英国船九点出发。 蓝色烟囱的法国船位于运送咖啡的船的左侧。 载可可的船的右侧是一艘前往马赛的船。 巴西船正驶向马尼拉。 载米的船旁边是一艘带绿色烟囱的船。 去热那亚的船五点出发。 西班牙船七点出发,在前往马赛的船的右侧。 带红色烟囱的船驶往汉堡。 七点出发的船旁边是一艘带白色烟囱的船。 边境的船载着玉米。 黑色烟囱的船八点出发。 运送玉米的船锚定在运送大米的船旁边。 去汉堡的船六点出发。

哪艘船开往塞得港?哪艘船运茶?

我在互联网上搜索答案,但找不到任何答案。所以我参考了“斑马拼图”,因此我为这个问题安排了代码。所以这是我的 Prolog 代码的问题。

exists(A,(A,_,_,_,_)).
exists(A,(_,A,_,_,_)).
exists(A,(_,_,A,_,_)).
exists(A,(_,_,_,A,_)).
exists(A,(_,_,_,_,A)).

rightOf(A,B,(B,A,_,_,_)).
rightOf(A,B,(_,B,A,_,_)).
rightOf(A,B,(_,_,B,A,_)).
rightOf(A,B,(_,_,_,B,A)).

middleShip(A,(_,_,A,_,_)).

lastShip(A,(_,_,_,_,A)).

nextTo(A,B,(B,A,_,_,_)).
nextTo(A,B,(_,B,A,_,_)).
nextTo(A,B,(_,_,B,A,_)).
nextTo(A,B,(_,_,_,B,A)).
nextTo(A,B,(A,B,_,_,_)).
nextTo(A,B,(_,A,B,_,_)).
nextTo(A,B,(_,_,A,B,_)).
nextTo(A,B,(_,_,_,A,B)).

solution(PortSaidShip, TeaCarrier) :-
   Shipes = (ship(_,_,_,_,_),ship(_,_,_,_,_),ship(_,_,_,_,_),ship(_,_,_,_,_),ship(_,_,_,_,_)),
   exists(ship('Greek',6,'Coffee',_,_),Shipes),
   middleShip(ship(_,_,_,_,'Black',_),Shipes),
   exists(ship('English',9,_,_,_),Shipes),
   rightOf(ship(_,_,'Coffee',_,_),ship('French',_,_,'Blue',_),Shipes),
   rightOf(ship(_,_,_,_,'Marseille'),ship(_,_,'Cocoa',_,_),Shipes),
   exists(ship('Brazilian',_,_,_,'Manila'),Shipes),
   nextTo(ship(_,_,_,'Green',_),ship(_,_,'Rice',_,_),Shipes),
   exists(ship(_,5,_,_,'Genoa'),Shipes),
   rightOf(ship('Spanish',7,_,_,_),ship(_,_,_,_,'Marseille'),Shipes),
   exists(ship(_,_,_,'Red','Hamburg'),Shipes),
   nextTo(ship(_,_,_,'White',_),ship(_,7,_,_,_),Shipes),
   lastShip(ship(_,_,'Corn',_,_),Shipes),
   exists(ship(_,8,_,'Black',_),Shipes),
   nextTo(ship(_,_,'Corn',_,_),ship(_,_,'Rice',_,_),Shipes),
   exists(ship(_,6,_,_,'Hamburg'),Shipes),
   exists(ship(PortSaidShip,_,_,_,'Port Said'),Shipes),
   exists(ship(TeaCarrier,_,'Tea',_,_),Shipes).

但是当我运行程序时它会说'false'。那么我该如何解决这个问题呢?谢谢

【问题讨论】:

您正在执行的返回 'false' 的 exact 查询是什么? 尝试分析解决方案的子目标。如果您有规则solution :- A, B, C, D, E.,它可能会失败,因为A 失败,或B 失败,或...,或E 失败。尝试检查各个目标以及它们给您带来的结果。 @MartynA:由于使用所有其他定义的唯一谓词的最一般查询已经失败,因此只能是那个查询。 @false:我们可能有不同的目的。您假设 OP 试图执行什么查询? solution(PortSaidShip, TeaCarrier) 这是最通用的一个。也就是说,任何更具体的方法也会失败。 【参考方案1】:

你问:

那我该如何解决呢?

以下是一种通用方法,它始终适用于像您这样的纯单调 Prolog 程序。您的实际问题是特定目标应该成功,但它失败了。所以你得到了一个意外失败。为了本地化您程序的负责部分,我们现在将系统地概括您的程序。一步步。直到我们有一个很小的程序片段。这种技术有时称为程序切片,有时称为程序修改

首先,将以下内容添加到您的代码中:

:- op(950, fy, *).
*_.

:- initialization(solution(_Port, _Carrier)).

现在我们将通过在其前面添加 * 来逐个删除目标,然后重新运行您的程序。所以准备好你将重新运行你的程序几次。要加载程序,请在顶层输入:

?- [shipes].

这几乎适用于任何地方,例如 SICStus、GNU、SWI、YAP。您现在将收到有关“失败指令”或类似内容的警告。所以 - 开心点 - 因为您现在可以轻松重现问题!

开始在最后一个目标处添加 *。你可以一次尝试几个。 要在修改后重新加载,您可以重新输入该目标,或者

在 SICStus 中,更好的状态 ensure_loaded(shipes). 这会检查文件是否已被修改,只有在重新加载后才会重新运行

在 SWI 中,输入 make.

最后得到如下程序片段:

中间船(A,(_,_,A,_,_))。 解决方案(PortSaidShip,TeaCarrier):- 船舶 = (船舶(_,_,_,_,_),船舶(_,_,_,_,_),船舶(_,_,_,_,_),船舶(_,_,_, _,_),船(_,_,_,_,_)), * exists(ship('Greek',6,'Coffee',_,_),Shipes), middleShip(ship(_,_,_,_,'Black',_),Shipes), * exists(ship('English',9,_,_,_),Shipes), * rightOf(ship(_,_,'Coffee',_,_),ship('French',_,_,'Blue',_),Shipes), * rightOf(ship(_,_,_,_,'Marseille'),ship(_,_,'Cocoa',_,_),Shipes), * 存在(ship('Brazilian',_,_,_,'Manila'),Shipes), * nextTo(ship(_,_,_,'Green',_),ship(_,_,'Rice',_,_),Shipes), * 存在(ship(_,5,_,_,'Genoa'),Shipes), * rightOf(ship('Spanish',7,_,_,_),ship(_,_,_,_,'Marseille'),Shipes), * exists(ship(_,_,_,'Red','Hamburg'),Shipes), * nextTo(ship(_,_,_,'White',_),ship(_,7,_,_,_),Shipes), * lastShip(ship(_,_,'Corn',_,_),Shipes), * 存在(ship(_,8,_,'Black',_),Shipes), * nextTo(ship(_,_,'Corn',_,_),ship(_,_,'Rice',_,_),Shipes), * exists(ship(_,6,_,_,'Hamburg'),Shipes), * exists(ship(PortSaidShip,_,_,_,'Port Said'),Shipes), * 存在(ship(TeaCarrier,_,'Tea',_,_),Shipes)

所以你需要看懂四行代码才能理解你的问题!

正如其他人已经指出的那样,问题是一旦您使用ship/6 和在其他情况下使用ship/5

另外一句话:(_,_,_,A,B) 最好写成[_,_,_,A,B],这是常用的列表表示法。

【讨论】:

为什么不使用 % ? "Why not use %": 单例变量警告,. 之前的最后一个目标不能这样处理,也不能用圆括号括起来。 是的,看起来是合法的。一些问题可以通过 /* */ 解决,但是通常情况下 cmets 可能会导致新问题,在其他编程语言中也是如此。【参考方案2】:

第二行(在解谓词之后)术语ship(...) 的参数数量是错误的。它是:

middleShip(ship(_,_,_,_,'Black',_),Shipes),

应该是这样的:

middleShip(ship(_,_,_,'Black',_),Shipes),

我没有检查这是否有效,但这肯定会导致您的求解器失败。

【讨论】:

建议更正后,?- solution(PortSaidShip, TeaCarrier). PortSaidShip = 'Spanish', TeaCarrier = 'French' ; false.

以上是关于为啥我无法通过 Prolog 获得 Ship Puzzle 的答案?的主要内容,如果未能解决你的问题,请参考以下文章

PROLOG通过条件获得名称

Prolog - 从一副牌中获得 X 个可能的牌

为啥我无法通过 Web 套接字连接获得 HubCallerContext 用户?

为啥我无法通过普通浏览器请求获得 ajax 请求的响应? [复制]

为啥我无法获得 /comments/new.html.erb?

为啥某些 .Net 程序集无法通过 AppDomain 的 GetAssemblies() 方法获得?