How to browse a list in Prolog to find a specific item

2

I'm a beginner in Prolog and I have questions about how to go through a list.

I have a historico(ra,[i1,i2,i3,...,in]) predicate where ra is the Academic Record of a student and each i is an item, with the form item(CM,SM,AN,NT,FQ) , CM being the code of the course, SM is the semester, AN is the year, NT the grade, and FQ the frequency.

Suppose I have the following fact:

historico(08080808,[item(1,1,2008,3.0,0.77),item(1,2,2008,6.5,0.90),item(5,1,2009,8.0,0.80)]).

I need to produce the list of names of extracurricular subjects taken by the learner, whose RA will be provided (the course code to be considered will be provided in a second parameter and the produced list must be returned in a third parameter); desired execution:

extra(08080808,1,QUAIS).

QUAIS = [anatomia] (considerando que existe uma materia(5,anatomia,6). )

I have the following rules already created:

curso(CODIGOCURSO,NOMECURSO).

materia(CODIGOMATERIA,NOMEMATERIA,CREDITOSMATERIA).

curriculo(CODIGOCURSO,[CODIGOMATERIA1,CODIGOMATERIA2,...,CODIGOMATERIAn).

aluno(RA,NOME).

cursa(RA,CODIGOCURSO).

historico(RA,[ITEM1,ITEM2,...,ITEMn).  (como mostrei anteriormente).

How can I do the extra(RA,CODIGOCURSO,LISTAMATERIAS) function? I want to compare each item in a student's history, subject matter, and curriculum. But how do you go through the list of items in the student's history?

    
asked by anonymous 23.06.2015 / 04:21

1 answer

3

There are two ways to go through a list, the most "natural" - using recursion, which you probably already know - or a combination of member with findall / bagof / setof . I'll show both forms, as they have both advantages and disadvantages (mostly readability and maybe in performance).

Recursion

First, it is necessary to determine if the M matter belongs to the C course:

pertence_curso(M,C) :-
    curriculo(C,Lista),
    member(M, Lista).

Next, if you cover all the subjects of the student checking if they belong to the course or not; if it belongs, is included in the list, otherwise you "jump" it and go pro:

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 with this method is that it will return repeated results (in its example, 1 was taken in the first and second semester, so it will be included in the final result twice). To get around this, you can either use an accumulator (ie it starts with the empty list, and it inserts items into it, but only inserts if it is not already there) or do the normal method and in the end delete the repeated items. >

member and setof

The idea behind this technique is to non-deterministically choose any element that satisfies the above conditions, and to 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 that's it! I chose a subject in the list of course subjects, then chose an item in the list of subjects taken by the student whose code is the same as the subject chosen. This predicate will succeed if such a combination exists, and will return only one result (a single subject). If this combination is not found, it will fail.

It's easy to see that if you call materia_extra on the terminal and ask for "more results" it will give you all the stuff that satisfies this condition, one at a time. But how to return all at once, in a list without repeating (i.e. a set)? Then enter setof :

extra(Aluno, Curso, Lista) :-
    setof(M, materia_extra(Aluno, Curso, M), Lista).

It will execute materia_extra/3 as many times as possible, and for each valid result, put the M (specified in the first argument) in the Lista list. In the case of setof it will only do this if the element no longer exists there ( bagof works the same, only accepts repeated elements; findall is equal to bagof , but does not fail if there is no result , returning an empty list instead).

    
23.06.2015 / 05:54