2

这是我在网上找到的一个非常简单的难题,我在网上找不到任何解决方案。规则很简单:

  • 有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:我已经尝试过实现斑马拼图,我意识到这比这个更容易解决......奇怪吗?
4

2 回答 2

3

我认为你在这里有一个错误

...
son(Artisan, Son),
...

正如您将儿子/ 2 关系定义为儿子(儿子,父亲)

编辑说,我会以完全不同的方式解决

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:我已经尝试过实现斑马拼图,但我意识到这比这个更容易解决......奇怪吗?”是的,这很奇怪,我认为斑马拼图比这个复杂得多

于 2014-05-21T14:44:01.293 回答
3

您过度工程化,并具体化了太多问题(典型的 Prolog 初学者错误)。你需要写的是一个六元谓词

jobs(BakerSrJob, BakerJrJob,
     CarpenterSrJob, CarpenterJrJob,
     MeatmanSrJob, MeatmanJrJob) :-
    ...

在正文中,对这些变量的约束。例如

member(BakerJrJob, [carpenter, meatman])

表示贝克小。要么是木匠,要么是肉工,并且

BakerJrJob \= BakerSrJob

表示贝克父子有不同的工作。member这些调用和约束中的几个\=应该足以编码所有必要的知识。(只需在变量名称中硬编码人员的身份,而不是将名称表示和检查为原子。如果您刚刚开始,请不要尝试编写通用的解谜程序。)

于 2014-05-21T11:47:03.510 回答