考虑以下不依赖于所有解决方案的内置插件,如findall
,setof
或bagof
:
% employees(Single Project, List of Employees
employees(Project, Employees) :-
employees(Project, [], Employees).
employees(Project, Acc, Employees) :-
( designer(Project, Employee, _)
; developer(Project, Employee, _)
),
\+ member(Employee, Acc), !,
employees(Project, [Employee|Acc], Employees).
employees(_Project, Employees, Employees).
此版本累积了从事项目工作的员工的唯一列表。同样,您的谓词的实现projects_of_all/2
可以是这样的:
% projects_of_all(List of Staff, List of Projects)
projects_of_all(Employees, Projects):-
projects_of_all(Employees, [], Projects).
projects_of_all(Employees, Acc, Projects):-
\+ var(Employees),
member(Employee, Employees),
( designer(Project, Employee, _)
; developer(Project, Employee, _)
),
\+ member(Project, Acc), !,
projects_of_all(Employees, [Project|Acc], Projects).
projects_of_all(_Employees, Projects, Projects).
请注意保护子目标\+ var(Employees)
,因为我们不希望调用的两个参数都 member(Employee, Employees)
完全未绑定,这可能会导致长度不断增加的列表中的变量无限递归扩展。一旦我们选择了一个Employee
,任何关联Project
的都会通过designer/3
or developer/3
(留下选择点)检索,直到Project
找到一个尚未累积的新的,此时我们去寻找更多;直到不再有,在这种情况下我们停止(第二个子句是基本情况)。
findall
虽然这可能相对于, setof
or的任何内部(即本机,非解释)实现而言效率低下bagof
,但它用于演示一种旨在帮助您理解使用累加器方法的解决方案的方法。
如果您需要使用内置的所有解决方案,您可以projects_of_all/2
这样实现:
% projects_of_all(List of Staff, List of Projects)
projects_of_all(Employees, Projects):-
findall(Project,
( member(Employee, Employees),
( designer(Project, Employee, _)
; developer(Project, Employee, _)
)
), ProjectsBag),
sort(ProjectsBag, Projects).
请注意,setof
andbagof
将回溯为您提供替代方案,但您希望累积列表中的所有项目,这是findall
. 不过,大概您不想要重复项,因此调用sort/2
显示的结果会删除重复项以给您一个集合。
编辑:在我写完这篇文章后,OP改变了(澄清)了这个问题,这需要一个完全不同的答案(下面的解释):
% projects_of_all(List of Staff, List of Projects)
projects_of_all(Employees, CommonProjects):-
% find the projects of every employee in the input list
employee_projects(Employees, EmployeeProjects),
% find the intersection of all projects (common projects)
recursive_val_intersect(EmployeeProjects, CommonProjects).
employee_projects([], []).
employee_projects([Employee|Employees], [Projects|Rem]) :-
findall(Project,
( designer(Project, Employee, _)
; developer(Project, Employee, _)
),
ProjectsBag),
sort(ProjectsBag, Projects),
employee_projects(Employees, Rem).
recursive_val_intersect([L|Ls], Intersect) :-
recursive_val_intersect(Ls, L, Intersect).
recursive_val_intersect([], Acc, Acc).
recursive_val_intersect([L0|Ls], L1, Intersect) :-
intersection(L0, L1, NewL),
recursive_val_intersect(Ls, NewL, Intersect).
employee_projects/2
用于构建Employee
输入列表中的每个项目都参与过的项目列表。请注意,它使用findall/3
我之前使用的解决方案策略。第二个谓词recursive_val_intersect/2,3
确定所有项目列表的交集,因为这表示每个员工一起工作的项目。这与上述解决方案不同,后者仅在输入列表中查找所有员工从事的所有项目,这正是我的目标。
请注意,recursive_val_intersect/3
上面依赖于 SWI-PROLOG set-intersection predicate intersection/3
,它接受没有重复的列表(因此使用sort/2
来构造输入列表employee_projects/2
)。