如何在Prolog列表中检查恰好有两个元素

3
% The structure of a musical group takes the form
% group(Groupname, Director, Players).
% Players is a (possibly empty) list of musician structures,
% but excludes the musician structure for the Director.
% The Director is also a musician in the group.
% musician structures take the form
% musician(Initials,Surname,
% cv(Years_as_professional,Previous_orchestra,Instrument)).

group(classicalstars,
      musician(w,mozart,cv(10,vienna_phil,piano)),
      [musician(j,haydn,cv(32,vienna_phil,cello)), musician(j,bach,cv(40,dresden_chamber,viola))]).
group(romantics,
      musician(l,beethoven,cv(32,vienna_phil,piano)),
      [musician(f,liszt,cv(10,vienna_phil,violin))]).
group(nordicsounds,
      musician(e,grieg,cv(50,bergen_phil,piano)),
      [ ]).
group(impressions,
      musician(g,faure,cv(40,paris_chamber,violin)),
      [musician(c,saint-saens,cv(51,paris_chamber,violin)), musician(m,ravel,cv(10,paris_chamber,piano)), musician(o,messiaen,cv(5,paris_chamber,violin))]).

director(X):- group(_,X,_).

musicians(X):- group(_,_,Musicians),
              member(X,Musicians).

exists(X):- director(X);
            musicians(X).

我正在尝试返回恰好有两位非导演小提琴手的乐团的指导者的姓和名。
目前我有以下代码:

 hasTwoViolinists(Initial,Surname):- group(_,musician(Initial,Surname,_),[musician(_,_,cv(_,_,violin)),musician(_,_,cv(_,_,violin))]).  

这将只返回一个只有两个小提琴手的乐团,这不是我要找的内容。
任何帮助都将不胜感激。


我理解印象组是正确的组,答案是(g,faure)。这正确吗? - Guy Coder
是的,在这种情况下,那就是我要寻找的答案。 - BobbyBorn2L8
你想要一个完整的解决方案还是只需要一些提示?我问这个问题是因为如果你想学习并看到完整的解决方案,这可能不会对你有太大的帮助。由你选择。 - Guy Coder
开始时给些建议会很不错。 - BobbyBorn2L8
我已经完成了除最后一条规则之外的所有工作。我相信一旦我解决了错误,它就会正常工作。因此,我将在答案中给出第一个提示,然后再提供更多提示。 - Guy Coder
1个回答

2
我做的第一件事是了解您的数据结构,并注意到找到一个有两个小提琴手的音乐家列表将成为最内层的规则,因此我开始处理这个问题。
以下是一个查找列表中恰好包含两个项目的规则。它需要改进以适用于小提琴手的音乐家列表。
find(C,_,C,[]).  % True when Count exactly equals Total and list is empty.

% When the head of the list is the item increment the count and do the next item.
find(Total,Item,Count,[H|T]) :-
      Item = H,
      Count1 is Count + 1,
      find(Total,Item,Count1,T).

% When the head of the list is not the item do the next item.
find(Total,Item,Count,[H|T]) :-
      Item \= H,
      find(Total,Item,Count,T).

% find(2,a,0,[a,b,a,c]).
% true .
%
% find(2,a,0,[a,a,a,c]).
% false.

注意:提示是剧透,可以通过将鼠标移动到它们上方来看到。
如果提示有代码解决方案,则在所有提示下方也作为剧透显示,因此您必须将鼠标移动才能查看它们。
提示1:

谓词是逻辑编程的好帮手。

提示2:

创建一个谓词,当音乐家是小提琴手时返回true,当音乐家不是小提琴手时返回false。

is_violinist(musician(o, messiaen, cv(5, paris_chamber, violin)))。
true。

is_violinist(musician(m, ravel, cv(10, paris_chamber, piano)))。
false。

提示3:
使用了两个谓词的“find”语句。其中一个是Item = H,另一个是Item \= H。如果将它们替换为“is_violinist”,会发生什么?参考链接:=\=\+表示“不是”。提示4:现在你可以用两个小提琴手找到音乐家组,那么怎样才能找到这个组呢?提示5:现在你已经找到了两个小提琴手所在的组,那么怎样才能找到导演的名字和姓氏呢?

提示代码。对于格式问题,markdown的限制,请见谅。

2.

is_violinist(musician(_,_,cv(_,_,violin))).

3.

% 当计数(Count)恰好等于总数(Total),并且列表为空时为真。
find_v(C,C,[]).

% 当列表头部的成员满足is_violinist条件时,增加计数并处理下一项。
find_v(Total,Count,[H|T]) :-
is_violinist(H),
Count1 is Count + 1,
find_v(Total,Count1,T).

% 当列表头部的成员不满足is_violinist条件时,处理下一项。
find_v(Total,Count,[H|T]) :-
\+ is_violinist(H),
find_v(Total,Count,T).

4.

1. 寻找乐队:find_group(Group) :- group(Group,_,Musicians), find_v(2,0,Musicians). 2. 寻找指挥:find_director(Initial,Surname) :- group(_,musician(Initial,Surname,_),Musicians), find_v(2,0,Musicians).

不应该使用 Count is Count + 1,因为你需要使用另一个变量来传递递增的计数到下一个规则中,例如 Count1。在Prolog中,变量不像命令式代码中那样,你不能不断地改变它们的值。请参阅:变量及其在Prolog中的设置和使用 - Guy Coder
参见:SWI-Prolog not/1。读作:“H不是小提琴手”。由于“H”是一位音乐家,因此\+ is_violinist(H)可以理解为:“一位不是小提琴手的音乐家”。 - Guy Coder
非常感谢,回想起来这很简单 XD - BobbyBorn2L8
它也很重要,因为它是三个“find”规则中的第一个。将其移动到第二或第三个位置,看看会发生什么。还要注意,find是一个谓词,它只返回true或false,而不是列表或项,因此是一个谓词。 - Guy Coder
find_v(C,C,[]). 是三个 find 规则中唯一返回真值的规则。使用谓词,一旦知道为真就可以停止搜索,但如果得到假值则必须继续搜索。 - Guy Coder
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接