Логически обработка списков
-
27-09-2019 - |
Вопрос
У меня есть большое количество фактов в моей программе, листит разработчиков и дизайнеров в компании, а также предыдущие проекты, как и так ..
% project(Project Name,Year)
project(efnet, 2007).
% designer(Project Name, Name, Role)
designer(efnet, jane_cole, lead).
% developer(Project Name, Name, Role)
developer(efnet, alex_tobbs, architect).
Я также создал определение, которое отображает список дизайнеров, либо разработчиков, работающих над проектом
% employees(Single Project, List of Employees
employees(Project, E)
Я хочу, чтобы создать новое определение, которое предпримет список дизайнеров, либо разработчиков, и отображает список всех названий проекта, на которых они оба работали (P); вот так..
% projects_of_all(List of Staff, List of Projects)
projects_of_all(S,P):- ...
Я могу сделать это легко с Findall (или Bagof), если я должен найти фильмы одного человека, но я не уверен, как я сделаю это со списком сотрудников. Может кто-нибудь позволить мне помогать с этим?
Решение
Рассмотрим следующее, что не полагается на встроенные вселогические решения, как 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
или developer/3
(оставив точки выбора), пока новый Project
еще не накапливается, когда мы идем искать больше; Пока нет еще больше, в этом случае мы останавливаемся (2-е место - это базовый случай).
Хотя это, вероятно, неэффективно относительно любых внутренних (то есть родных, не интерпретированных) реализации findall
, setof
или 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
а также bagof
будет возвращаться, чтобы дать вам альтернативы, но вы хотите, чтобы все проекты в списке накоплены, что является поведением findall
. Отказ Предположительно, хотя вы не хотите дубликаты, так что звонит sort/2
В результате, как показано, удаляет дубликаты, чтобы дать вам набор.
РЕДАКТИРОВАТЬ: ОП изменился (уточненный) вопрос после того, как я написал это, который призвал совершенно другой ответ (объяснение ниже):
% 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/3
, который принимает списки без дубликатов (следовательно, использование sort/2
Чтобы построить входные списки в employee_projects/2
).
Другие советы
Попробуйте что-то подобное на это, где Es
это список сотрудников:
setof(P, E^(member(E, Es), employee(P, E)), Projects)
То E^
это экзистенциальный квантов.