Prolog 谜题面包师、肉工和木匠
Posted
技术标签:
【中文标题】Prolog 谜题面包师、肉工和木匠【英文标题】:Prolog puzzle baker, meatman and carpenter 【发布时间】:2014-07-10 00:47:46 【问题描述】:这是我在网上找到的一个非常简单的谜题,我在网上找不到任何解决方案。规则很简单:
有 6 位工匠,M.Baker 和他的儿子,M.Carpenter 和他的儿子,M.Meatman 和他的儿子 每个工匠都可以是面包师、木匠或肉匠 儿子和父亲不能做同样的工作 姓氏不能是工作名称(M.Baker 和他的儿子不能是面包师)我们知道: - M.Meatman 的儿子是面包师 - M.Baker 和 M.Carpenter 的儿子做同样的工作
我实现了这个谓词:
% swipl prolog
% M.Meatman's son is baker
% M. Baker do job of M. Carpenter's son
% jobs
job(baker).
job(meatman).
job(carpenter).
% fathers
artisan(fatherbaker).
artisan(fathercarpenter).
artisan(fathermeatman).
% sons
artisan(sonbaker).
artisan(soncarpenter).
artisan(sonmeatman).
% some links
father(fatherbaker, sonbaker).
father(fathermeatman, sonmeatman).
father(fathercarpenter, soncarpenter).
son(S, F) :- father(F, S).
same_name(fathercarpenter, soncarpenter, carpenter).
same_name(fathermeatman, sonmeatman, meatman).
same_name(fatherbaker, sonbaker, baker).
% rules:
do_job(Artisan, Job) :-
Artisan==sonmeatman,!,
artisan(Artisan),
job(Job),
Job=baker. % M.Meatman's son is baker (rule 1)
do_job(Artisan, Job) :-
Artisan==fatherbaker,!,
artisan(Artisan),
job(Job),
do_job(soncarpenter, Job). % M.Baker do M.Carpenter's son job
% not relevant...
%do_job(Artisan, Job) :-
% Artisan == soncarpenter,!,
% job(Job),
% artisan(Artisan),
% do_job(fatherbaker, Job). % rule 2 inverted
% checking if father job is not the same and name are not forgotten
do_job(Artisan, Job) :-
artisan(Artisan),
job(Job),
father(Father, Artisan),
do_job(Father, JobFather),
Job \== JobFather,
not(same_name(Artisan,_,Job)).
% checking if son job is not the same and name are not forgotten
do_job(Artisan, Job) :-
artisan(Artisan),
job(Job),
son(Artisan, Son),
do_job(Son, JobSon),
Job \== JobSon,
not(same_name(_, Artisan, Job)).
那我试试:
swipl
?- do_job(sonmeatman, X).
X = baker ;
false.
?- do_job(fatherbaker, X).
false.
你能告诉我哪里错了吗?
注意:我是 Prolog 的新手。我以前从未使用过这种语言(我是 Golang、Python、C 程序员......)。 注 2:请原谅我的英语,我刚刚翻译了我的法语示例,可能工作名称或动词不正确... 注 3:我已经尝试过实现斑马拼图,但我意识到这比这个更容易解决……奇怪吗?【问题讨论】:
【参考方案1】:您过度设计,reifying 问题太多(典型的 Prolog 初学者错误)。你需要写的是一个六元谓词
jobs(BakerSrJob, BakerJrJob,
CarpenterSrJob, CarpenterJrJob,
MeatmanSrJob, MeatmanJrJob) :-
...
在正文中,对这些变量的约束。例如
member(BakerJrJob, [carpenter, meatman])
表示 Baker jr.要么是木匠,要么是肉工,并且
BakerJrJob \= BakerSrJob
表示贝克父子有不同的工作。这些member
调用和\=
约束中的几个应该足以编码所有必要的知识。 (只需在变量名称中硬编码人员的身份,而不是将名称表示和检查为原子。如果您刚刚开始,请不要尝试编写通用的解谜程序。)
【讨论】:
好的,今晚我会检查一下(法国是 14:50 :)) - 我很确定你是对的,准确地说,我之前尝试过其他一些例子......我对层次解析、路径解析等没有任何问题……我只是尝试阅读文档、测试等等。 经过一些测试,我确实填充这种方式比CappelliC解决方案“更简单”。它易读、易于理解并且只返回一个解决方案。 CapelliC 解决方案很好,但返回许多相同但顺序不同的解决方案。【参考方案2】:我认为你这里有错误
...
son(Artisan, Son),
...
正如您将儿子/2 关系定义为儿子(儿子,父亲)
edit 说,我会以完全不同的方式解决
solve(Puzzle) :-
% There are 6 artisans, M.Baker and his son, M.Carpenter and his son and M.Meatman and his son
Puzzle = [N1/F1/S1, N2/F2/S2, N3/F3/S3],
% Each artisan can be baker, carpenter or meatman
Symbols = [baker, carpenter, meatman],
permutation([N1,N2,N3], Symbols),
permutation([F1,F2,F3], Symbols),
permutation([S1,S2,S3], Symbols),
% Son and father cannot do the same job
foreach(member(_/F/S, Puzzle), F\=S),
% The lastname couldn't be the job name
foreach(member(N/F/S, Puzzle), (N\=F, N\=S)),
% M.Meatman's son is baker
member(meatman/_/baker, Puzzle),
% M.Baker do the same job than M.Carpenter's son
member(baker/J/_, Puzzle),
member(carpenter/_/J, Puzzle).
foreach/2 这是一个棘手的谓词,您可以尝试用自己的(递归)定义替换。 permutation/2 更简单,可以轻松替换...
最后一点:“注 3:我已经尝试过实现斑马拼图,但我意识到解决这个问题比这个更简单......奇怪?”是的,这很奇怪,我认为斑马拼图要复杂得多比这个
【讨论】:
"如果Goal
不包含任何不与Generator
共享的变量,则实现执行forall/2
。"看起来 forall
可以用在那里代替。
@WillNess:你说得对,我的错
不知道 X/Y/Z 表示法,与 python 元组相媲美...真不错!
@Metal3d:您可以在 Prolog 中使用任何 binop 作为对构造函数。 a+b+c
也可以,或者(A, B, C)
。以上是关于Prolog 谜题面包师、肉工和木匠的主要内容,如果未能解决你的问题,请参考以下文章