在Prolog中比较列表元素结构

时间:2014-05-11 23:07:01

标签: prolog

我在学习期间使用SWI-Prolog处理示例问题。我已经到了这个问题的最后一部分,我必须递归(我希望)比较包含'研究者'结构的列表的元素,以确定研究人员是否具有相同的姓氏,如果他们这样做,则返回Forename和该名单的组长的姓氏。

只有一个符合此条件的列表,它有四个成员,都具有相同的姓氏。但是,正确的答案会返回四次。我觉得我的解决方案不够优雅且缺乏。这是一个问题:

以下Prolog数据库代表主题教学团队。

% A research group structure takes the form
% group(Crew, Leader, Assistant_leader).
%
% Crew is a list of researcher structures,
% but excludes the researcher structures for Leader
% and Assistant_leader.
%
% researcher structures take the form
% researcher(Surname, First_name, expertise(Level, Area)).

group([researcher(giles,will,expertise(3,engineering)),
researcher(ford,bertha,expertise(2,computing))],
researcher(mcelvey,bob,expertise(5,biology)),
researcher(pike,michelle,expertise(4,physics))).

group([researcher(davis,owen,expertise(4,mathematics)),
researcher(raleigh,sophie,expertise(4,physics))],
researcher(beattie,katy,expertise(5,engineering)),
researcher(deane,fergus,expertise(4,chemistry))).

group([researcher(hardy,dan,expertise(4,biology))],
researcher(mellon,paul,expertise(4,computing)),
researcher(halls,antonia,expertise(3,physics))).

group([researcher(doone,pat,expertise(2,computing)),
researcher(doone,burt,expertise(5,computing)),
researcher(doone,celia,expertise(4,computing)),
researcher(doone,norma,expertise(2,computing))],
researcher(maine,jack,expertise(3,biology)),
researcher(havilland,olive,expertise(5,chemistry))).

根据此信息,编写可用于返回以下内容的Prolog规则(以及所需的任何其他谓词):

任何领导者的名字和姓氏,其船员人数不止一个且姓氏相同。 [4分]

这是我目前使用递归的解决方案,虽然它对于列表的每个成员来说都是不必要的低效率,但它将该成员与其他每个成员进行比较。因此,正确的列表是四个成员长,它返回'jack maine'四次。

surname(researcher(S,_,_),S).

checkSurname([],Surname):-
    Surname==Surname. % base case 

checkSurname([Researcher|List],Surname):-
    surname(Researcher,SameSurname),
    Surname == SameSurname,
    checkSurname(List,SameSurname).

q4(Forename,Surname):-
    group(Crew,researcher(Surname,Forename,_),_),
    length(Crew,Length),
    Length > 1,
    member(researcher(SameSurname,_,_),Crew),
    checkSurname(Crew,SameSurname).

如果没有重复的结果,并且每次都没有多余地将每个成员与每个其他成员进行比较,我怎么能这样做呢?对于我采取的每一种方法,每次将'SameSurname'留作单身时,我都会被抓住,因此必须在q4谓词中强制使用它两次。

当前输出

    13 ?- q4(X,Y).
    X = jack,
    Y = maine ;  x4

3 个答案:

答案 0 :(得分:2)

紧凑而有效的解决方案:

q4(F, S) :-
    group([researcher(First,_,_), researcher(Second,_,_)| Crew], researcher(S, F, _), _),
    \+ (member(researcher(Surname, _, _), [researcher(Second,_,_)| Crew]), First \== Surname).

示例调用(产生单个解决方案):

?- q4(X,Y).
X = jack,
Y = maine.

答案 1 :(得分:1)

你做得比以前更复杂。您的q4/2可能更简单:

q4(First_name, Surname) :-
    group(Crew, researcher(Surname, First_name, _E), _A),
    length(Crew, Len), Len > 1,
    all_same_surname(Crew).

现在您只需要定义all_same_surname/1。这个想法很简单:取第一名船员的姓氏,并将其与其余船员的姓氏进行比较:

all_same_surname([researcher(Surname, _FN, _E)|Rest]) :-
    rest_same_surname(Rest, Surname).

rest_same_surname([], _Surname).
rest_same_surname([researcher(Surname, _FN, _E)|Rest), Surname) :-
    rest_same_surname(Rest, Surname).

(显然,如果没有船员,all_same_surname/1会立即失败)

这应该是它,除非我误解了问题陈述。

?- q4(F, S).
F = jack,
S = maine.

那怎么样?

注意:解决方案只需采用最直接的方法来回答问题并且易于编写和阅读。有很多东西可以做其他事情。由于没有理由不这样做,我在谓词的头部使用了模式匹配和统一,而不是在正文中进行比较或从复合词中提取参数的额外谓词。

P.S。考虑一下member/2的作用(甚至在库中查找其定义),您将看到解决方案中所有额外选择点的来源。

答案 2 :(得分:1)

鲍里斯已经回答了这个问题,但我想展示一下我能提供的最简洁的解决方案。这仅用于教育目的(推广findall/3maplist/2):

q4(F, S) :-
    group(Crew, researcher(S, F, _), _),
    findall(Surname, member(researcher(Surname, _, _), Crew), Surnames),
    Surnames = [ First, Second | Rest ],
    maplist(=(First), [ Second | Rest ]).