Prolog : append to list - list

I have facts like that :
student(stud01, 'Programming 1', 90).
student(stud01, 'Math 1', 78).
student(stud01, 'Statistics 1', 94).
student(stud01, 'Electronics 1', 81).
student(stud01, 'Management', 66).
student(stud01, 'English', 83).
i want to build a predict that take the student id and and put all its grade in list
and after that take the max grade
i try this :
stuGrade(ID,List):-
stuGrade([],ID,List).
stuGrade(TmpList,ID,List):-
student(ID,Course,Grade),
not(mem([ID],TmpList)), !,
append_List([Grade],TmpList,NewTmpList),
stuGrade(List,ID,NewTmpList).
stuGrade(List,ID,List).
maxStudentGrade(ID,Max):-
stuGrade(ID,L),
max(L,Max).
but the problem that after using ?-trace. i see that the output list containing only the first grade (90) how can i slove this problem

Suppose that you begin with ?- stuGrade(stu01, List).
Then you will get the first student entry at least. And you say there is some issue after that.
So, a simple way to check is to test by using the first part of the query, such that,
?- ID = stu01, TmpList = [],
student(ID,Course,Grade),
not(mem([ID],TmpList)), !.
When you find its result is false, stop and check the last predicate.
Using break points
trace/0 and nodebug/0 are nice tools.
You may want to check issues by like the following,
stuGrade(TmpList, ID, List) :-
student(ID, Course, Grade),
trace, % <------------------ Put a break point here.
not(mem([ID], TmpList)),
nodebug, % <----------------- Here, stop tracing.
!,
append_List([Grade], TmpList, NewTmpList),
stuGrade(List, ID, NewTmpList).
Good luck.

And you code...
stuGrade(ID,List):- stuGrade([],ID,List).
stuGrade(TmpList, ID, List):-
student(ID, Course, Grade),
not(mem([ID], TmpList)),
!,
append_List([Grade], TmpList, NewTmpList),
stuGrade(List, ID, NewTmpList).
stuGrade(List,ID,List).
Not bad. But, consider the first clause of stuGrade/3.
When you get a Grade, it's prepended to TmpList and you get NewTmpList. But at the next line, the newly built list is put as the third argument, as you know that it's for the final returned List.
It cannot be that. The first parameter of stuGrade/3, TmpList, is the accumulating parameter. Then NewTmpList might be placed as the first argument of the recursive query, such that stuGrade(NewTmpList, ID, List).

Related

Prolog - searching a list inside a predicate

I have predicates of students and sports they do, and I want to find out which students do a particular sport. I have this sofar, but i can only get results if I enter exact sports in a list , and my find predicate works only to find a sport in a list. I don't know how to put it together to use to find students that do 1 sport:
student('Quinton Tarentino', male, 12).
student('Tom Hanks', male, 9).
student('Ed Harris', male, 11).
does_sport('Quinton Tarentino', [soccer, hockey, cricket]).
does_sport('Tom Hanks', []).
does_sport('Ed Harris', [hockey, swimming]).
sports([soccer, hockey, swimming, cricket, netball]).
find(X) :- sports(L), member(X, L).
I tried things like:
?- does_sport(X, find(soccer, L)).
This just returns false. I know I need to link my sports list to the does_sports predicate but not sure how.
Any advice appreciated :)
To find out which students do a particular sport, you could define a predicate like so:
student_sport(St,Sp) :-
does_sport(St,L), % L is a list of sports student St does
member(Sp,L). % Sp is a member of list L
Then you can query for e.g. soccer, as you seem to intend in your question, like so:
?- student_sport(St,soccer).
St = 'Quintin Tarentino' ? ;
no
Hockey on the other hand yields two results:
?- student_sport(St,hockey).
St = 'Quintin Tarentino' ? ;
St = 'Ed Harris' ? ;
no
If you want to have a list of students doing hockey instead, you can use findall/3 like so:
?- findall(St,student_sport(St,hockey),L).
L = ['Quintin Tarentino','Ed Harris']
Or alternatively setof/3 to get a sorted list (without duplicates, in case you happened to have facts that contain any):
?- setof(St,student_sport(St,hockey),L).
L = ['Ed Harris','Quintin Tarentino']
Note that in some Prologs you might have to explicitly include a library to use member/2, e.g. in Yap: :- use_module(library(lists))., while others autoload it, e.g. SWI.
EDIT:
Concerning the issues you raised in your comment, let's maybe start with your observation that student_sport/2 produces the answers one at a time. That is intentional, as suggested by the predicate name that contains the word student in singular: It describes a relation between a student and a particular sport that very student practices. That's why I added the example queries with findall/3 and setof/3, to show ways how you can collect solutions in a list. You can easily define a predicate students_sport/2 that describes a relation between a particular sport and a list of all students who practice it:
students_sport(L,Sp) :-
setof(St,student_sport(St,Sp),L).
Concerning the sports-austere, you can choose an atom to denote that case, say none and then add an according rule to student_sport/2 like so:
student_sport(St,none) :- % <- rule for the sports-austere
does_sport(St,[]). % <- succeeds if the student does no sport
student_sport(St,Sp) :-
does_sport(St,L),
member(Sp,L).
This yields the following results:
?- student_sport(St,none).
St = 'Tom Hanks' ? ;
no
?- students_sport(St,none).
St = ['Tom Hanks']
?- students_sport(St,hockey).
St = ['Ed Harris','Quintin Tarentino']
?- students_sport(St,Sp).
Sp = cricket,
St = ['Quintin Tarentino'] ? ;
Sp = hockey,
St = ['Ed Harris','Quintin Tarentino'] ? ;
Sp = none,
St = ['Tom Hanks'] ? ;
Sp = soccer,
St = ['Quintin Tarentino'] ? ;
Sp = swimming,
St = ['Ed Harris']
And finally, concerning your assumption of your code being exactly as I wrote it: There is a similarity in structure, namely your predicate find/1 having a first goal (sports/1) involving a list and subsequently using member/2 to check for membership in that list. The second rule (or single rule before the edit) of student_sport/2 is also having a first goal (but a different one: does_sport/2) involving a list and subsequently using member/2 to check for membership in that list. Here the similarities end. The version I provided is not using sports/1 at all but rather the list of sports associated with a particular student in does_sport/2. Note that find/1 does not describe any connection to students whatsoever. Furthermore your query ?- does_sport(X, find(soccer, L)). indicates that you seem to expect some sort of return value. You can regard predicates as functions returning true or false but that is usually not very helpful when programming Prolog. The argument find(soccer,L) is not being called as you seem to expect, but literally passed as an argument. And since your facts do not include something along the lines of
does_sport(*SomeStudentHere*, find(soccer,L)).
your query fails.

prolog : using elements of a list in a fact

I want to make a Prolog console application that shows venue name and rent for any event for a given(user input) location. So far I have been able to create a list of venues of a given(user input) location.
My code is given below.
listing(venue_location).
listing(venue_seating).
listing(venue_overhead).
venue_location(washington_party_hall1, washington).
venue_location(washington_party_hall2, washington).
venue_location(newyork_party_hall1, newyork).
venue_location(newyork_party_hall2, newyork).
venue_capacity(washington_party_hall1,300).
venue_capacity(washington_party_hall2,450).
venue_capacity(newyork_party_hall1, 340).
venue_capacity(newyork_party_hall2,500).
venue_rent(washington_party_hall1,1350).
venue_rent(washington_party_hall2,1200).
venue_rent(newyork_party_hall1,1500).
venue_rent(newyork_party_hall2,1000).
main:-
write("Enter a location"),nl,write("->"),
read(Loc),
findall(X,venue_location(X,Loc),Venues),
write(Venues).
This gives the following output:
Enter a location
->washington.
[washington_party_hall1,washington_party_hall2]
true.
Is there a way to use the venues list in venue_rent fact to get every list element's rent?
I want to display the name of these venues with their rents in this way:
washington_party_hall1 1350
washington_party_hall2 1200
You know that if you have a given venue, say Venue, you can get the rent from your facts:
?- Venue = washington_party_hall1, venue_rent(Venue, Rent).
Rent = 1350
If you want to get the rent for all of the venues in a list Venues, you can use member/2:
?- Venues = [washington_party_hall1,washington_party_hall2], member(Venue, Venues), venue_rent(Venue, Rent).
That's the basic query structure of what you want. Now if you want to write things to the terminal in a nice format, you can use a failure driven loop, which is a common way to display a list of items in a formatted fashion:
write_venue_rents(Venues) :-
member(Venue, Venues),
venue_rent(Venue, Rent),
write(Venue), write(' '), write(Rent), nl,
fail.
This doesn't give you aligned columns, but you get the idea. You can look up the formatting options and predicates for Prolog to format the output to your taste.
Another way is to use maplist/2:
% Write out the rent for a given venue
write_venue_rent(Venue) :-
venue_rent(Venue, Rent),
write(Venue), write(' '), write(Rent), nl.
% Write out the rent for a list of venues
write_venue_rents(Venues) :-
maplist(write_venue_rent, Venues).
I think the maplist/2 version is more elegant.

Make list from list of lists with condition

I'm currently stucked with recursion over lists of lists.
The task is quite simple in any imperative language: iterate over every professor, iterate over professor's course list, and get every course that matches CourseNumber given as predicate argument to output var CourseList.
I have simple solution (other ones just fail with out of stack or returns empty list), but as you can see, it checks only if the head of the course's list matches cond.
get_teaching_courses(CourseNumber, CourseList) :-
findall(Course,
(
professor(_, [Course | _]),
member(CourseNumber, Course)
),
CourseList).
professor fact has next struct:
professor(Name, [ [CourseName , CourseNumber], .... ]).
I am thinking of making predicate over predicate, but I can't achieve it (something wrong with append I guess).
It's been like 2 days I've started learning prolog, and if you can give me any help, advice or link that can help me, I'd appreciate it.
example:
assertz(
professor(
'Bob',
[
['Math', 2],
['PE', 3]
]
)
).
Solution:
get_teaching_courses(CourseNumber, CourseList) :-
findall(CourseName,
(
professor(_, Course),
member([CourseName, CourseNumber], Course)
),
CourseList).
thanks to #CapelliC and his answer.
the problem is the incorrect pattern matching applied in member/2. Try
member([_,CourseNumber], Course)

Add fact to list if it's not already in

I need to construct a list based on facts I have. For example I have a course list like this :
attend(student1,c1).
attend(student1,c2).
attend(student2,c1).
attend(student2,c3).
Now I want to have a predicate courselist/2 which returns a list of courses for a given student. Of course every course should be in this list only once. I can't use built-in predicates like findall but I can use member or append. So far I have something like this :
courselist(S,R) :- attend(S,C), member(C,R), courselist(S,R).
courselist(S,R) :- attend(S,C), append([C],L,R), courselist(S,R).
and I know that's wrong but I don't know how to find all facts without getting into an endless loop.
It's a silly restriction that you cannot use findall/3, because it is a natural solution for such a problem. You can do it manually like this:
student_courses(Student, Courses) :-
student_courses(Student, [], Courses).
student_courses(S, Cs0, Cs) :-
( attend(S, C), \+ member(C, Cs0) ->
student_courses(S, [C|Cs0], Cs)
; Cs = Cs0
).
Example query:
?- student_courses(student2, Cs).
Cs = [c3, c1].
Note that this is not a true relation, since this particular solution does not appear in the following more general query:
?- student_courses(Student, Cs).
Student = student1,
Cs = [c2, c1].
I leave it as an exercise for you to implement it in such a way that the most general query yields all correct results. Also notice the more readable and relational predicate names.

Prolog list adding

--the question has been edited--
Using this data, I need to create a list:
team(milan,1).
team(napoli,2).
team(lazio,3).
team(roma,4).
team(inter,4).
team(juventus,5).
So, given a query like:
check([milan,lazio,roma,inter]).
make a new list with their respective team number.
X=[1,3,4,4]
What I'm trying to do is creating a list, adding elements one at a time.
check([H|T]) :-
team(H,R),
append([R],_, X),
check(T).
Could someone help me complete this?
You need to find all the team numbers for which the name of the team is a member of the list of team names that you are interested in:
?- findall(Number, (
team(Name, Number),
member(Name, [milan, lazio, roma, inter])), Numbers).
Numbers = [1, 3, 4, 4].
To return the numbers in a given order, just apply member/2 before team/2, in this case member/2 generates names (in the given order), and team/2 maps them to numbers:
?- findall(Number, (
member(Name, [lazio, milan, inter]),
team(Name, Number)), Numbers).
Numbers = [3, 1, 4].
A lot of time since I used Prolog but an answer -more or less- would look like:
check([]) :- true.
check([X]) :- team(X,_).
check([X,Y]) :- team(X,N), team(Y,M), N < M.
check([X,Y|T]) :- check(X,Y), check([Y|T]).
See this question for a very similar problem.
From what you say you might be better off making a list and then sorting it. That way you'd know the list is in order. Of course it's tricky in that you are sorting on the team ranks, not the alphabetic order of their names.
But the question you asked is how to check the list is in sorted order, so let's do it.
check([ ]). % just in case an empty list is supplied
check([_]). % singleton lists are also in sort order
check([H1,H2|T]) :-
team(H1,R1),
team(H2,R2),
R1 <= R2,
check([H2|T]).
Note that the recursion reduces lists with at least two items by one, so the usual termination case will be getting down to a list of length one. That's the only tricky part of this check.
Added in response to comment/question edit:
Sure, it's good to learn a variety of simple "design patterns" when you are getting going with Prolog. In this case we want to "apply" a function to each item of a list and build a new list that contains the images.
mapTeamRank([ ],[ ]). % image of empty list is empty
mapTeamRank([H|T],[R|S]) :-
team(H,R),
mapTeamRank(T,S).
So now you have a predicate that will turn a list of teams LT into the corresponding list of ranks LR, and you can "check" this for sorted order by calling msort(LR,LR):
check(LT) :-
mapTeamRank(LT,LR),
msort(LR,LR).