Pergunta

Tenho um grande número de fatos em meu programa, listando desenvolvedores e designers de uma empresa, bem como projetos anteriores, como este.

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

Também criei uma definição que exibe uma lista de designers ou desenvolvedores trabalhando em um projeto

% employees(Single Project, List of Employees
employees(Project, E)

O que eu quero é criar uma nova definição que pegue uma lista de designers ou desenvolvedores e exiba uma lista de todos os títulos de projetos em que AMBOS trabalharam (P);igual a..

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

Posso fazer isso facilmente com findall (ou bagof) se precisar encontrar os filmes de uma única pessoa, mas não tenho certeza de como faria isso com uma lista de funcionários.Alguém pode me dar uma ajuda com isso?

Foi útil?

Solução

Considere o seguinte, que não depende de todas as soluções integradas, como findall, setof ou 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).

Esta versão acumula uma lista única de funcionários trabalhando em um projeto.Da mesma forma, a implementação do seu predicado projects_of_all/2 pode ser assim:

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

Observe o subobjetivo da guarda \+ var(Employees), já que não queremos ambos os argumentos para a chamada para member(Employee, Employees) ser completamente desvinculado, o que pode causar uma expansão infinitamente recursiva de variáveis ​​em listas de comprimento cada vez maior.Uma vez que selecionamos um Employee, qualquer associado Project é recuperado através designer/3 ou developer/3 (deixando pontos de escolha), até um novo Project ainda não acumulado é encontrado, momento em que vamos procurar mais;até que não haja mais, nesse caso paramos (a 2ª cláusula é o caso base).

Embora isso seja provavelmente ineficiente em relação a qualquer implementação interna (ou seja, nativa, não interpretada) de findall, setof ou bagof, serve para demonstrar uma abordagem que visa ajudá-lo a entender a solução usando métodos acumuladores.

Se você precisar do uso de todas as soluções integradas, poderá implementar projects_of_all/2 Como tal:

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

Observe que setof e bagof voltará atrás para lhe dar alternativas, mas você quer que todos os projetos em uma lista sejam acumulados, que é o comportamento de findall.Presumivelmente, porém, você não quer duplicatas, então ligar sort/2 no resultado mostrado, remove duplicatas para fornecer um conjunto.

EDITAR: O OP alterou (esclareceu) a pergunta depois que eu escrevi isso, o que exigia uma resposta completamente diferente (explicação abaixo):

% 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 é usado para construir uma lista de listas de projetos que cada Employee na lista de entrada havia trabalhado.Observe que ele usa o findall/3 estratégia de solução que usei anteriormente.O segundo predicado, recursive_val_intersect/2,3, determina a intersecção de todas as listas de projetos, pois indica os projetos que cada funcionário possui trabalhamos juntos.Isso é diferente da solução acima, que busca apenas todos os projetos trabalhados por todos os funcionários na lista de entrada, que é o que eu pretendia.

Observe que recursive_val_intersect/3 acima depende do predicado de interseção de conjunto SWI-PROLOG intersection/3, que pega listas sem duplicatas (daí o uso de sort/2 para construir as listas de entrada em employee_projects/2).

Outras dicas

Tente algo semelhante a isso, onde Es é a lista de funcionários:

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

o E^ é um quantificador existencial.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top