Логическая обработка списков - PullRequest
2 голосов
/ 30 октября 2010

У меня есть большое количество фактов в моей программе, перечисляющих разработчиков и дизайнеров в компании, а также предыдущие проекты, например, так:

% 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)

Я хочу создать новое определение, которое будет принимать список дизайнеров или разработчиков и отображать список всех названий проектов, с которыми они ОБА работалина стр);вот так ..

% projects_of_all(List of Staff, List of Projects)
projects_of_all(S,P):- ...

Я могу легко сделать это с помощью findall (или bagof), если мне нужно найти фильмы одного человека, но я не уверен, как мне это сделать с помощью спискаработники.Может ли кто-нибудь помочь мне с этим?

Ответы [ 2 ]

1 голос
/ 02 ноября 2010

Рассмотрим следующее, которое не зависит от встроенных встроенных решений, таких как 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 intersection/3, который принимает списки без дубликатов (следовательно, использование sort/2 для построения входных списков в employee_projects/2).

0 голосов
/ 30 октября 2010

Попробуйте что-то похожее на это, где Es - список сотрудников:

setof(P, E^(member(E, Es), employee(P, E)), Projects)

E^ - это экзистенциальный квантификатор.

...