There are two ways to go through a list, the most "natural" - using recursion, which you probably already know - or through a combination of member
with findall
/bagof
/setof
. I will show both forms, as they have advantages and disadvantages (in legibility, mainly, and maybe in the performance).
Recursion
Firstly, it is necessary to determine whether the matter M
belongs to the course C
:
pertence_curso(M,C) :-
curriculo(C,Lista),
member(M, Lista).
Then, if you go through all the subjects of the student checking if they belong to the course or not; if it belongs, it is included in the list, otherwise you "jump" it and go to the next:
extra(Aluno, Curso, R) :-
historico(Aluno, Itens),
percorre_itens(Itens, Curso, R).
% Se acabaram os itens, retorna a lista vazia
percorre_itens([],_,[]).
% Se a matéria pertence ao curso, inclui ela no resultado
percorre_itens([item(CM,_,_,_,_)|Resto], Curso, [Materia|Resultado]) :-
pertence_curso(CM, Curso),
!,
materia(CM, Materia, _),
percorre_itens(Resto, Curso, Resultado).
% Se ela não pertence ao curso (cláusula anterior falhou), não inclui
percorre_itens([_|Resto], Curso, Resultado) :-
percorre_itens(Resto, Curso, Resultado).
The problem of this method is that it will return repeated results (in your example, the matter 1
was attended in the first and second semester, so she will be included in the final result twice). To get around this, you can either use an accumulator (i.e. start with the empty list, and insert items into it, but only insert if it is no longer there) or do the normal method and at the end delete the repeated items.
Member and setof
The idea behind this technique is to choose not deterministically any element that satisfies the conditions cited, and ask for "more solutions" until all possibilities are exhausted:
materia_extra(Aluno, Curso, Materia) :-
curriculo(Curso, Materias),
member(CM, Materias),
historico(Aluno, Itens),
member(item(CM,_,_,_,_), Itens),
materia(CM, Materia, _).
And this is it! I chose a course on the course list, then chose an item on the course list from the student whose code is the same as the chosen course. This predicate will succeed if such a combination exists, and will return only one result (a single matter). If that combination is not found, it will fail.
It’s easy to see that if you call materia_extra
in the terminal and asking for "more results" he will deliver you all the materials that satisfy this condition, one at a time. But how to return them all at once, in a list without repetition (i.e. a set)? Therein comes the setof
:
extra(Aluno, Curso, Lista) :-
setof(M, materia_extra(Aluno, Curso, M), Lista).
He will execute the materia_extra/3
as many times as possible and, for each valid result, put the M
(specified in the first argument) in the list Lista
. In the case of setof
it will only do so if the element no longer exists there (the bagof
works the same, only it accepts repeated elements; the findall
is equal to bagof
, but does not fail if there is no result, returning an empty list instead).
Your reply was very explanatory, thank you! I have another question regarding this very knowledge bank... can I ask here or should I open a new question?
– Gabriel Polo
If my answer solved your original question, and that’s a separate question, then I’d better open another question.
– mgibsonbr