Prolog - searching a list inside a predicate - list

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.

Related

return elements from list of lists - prolog

I have a list of lists that contain titles and scores like : [ [ 'title1',100 ],[ 'title2',200 ],...]. I have to make a predicate that returns the title if I have a certain score. find_title(Score,List,ListOfTitles). Also some scores may be the same that's why I am trying to store it in a List..in case there is more than one titles with the same score.
I tried this :
return_title(Score,List,[H|T]):-
return_title(Score,List,T),
member([X,Score],List),
H=X.
But it doesn't work..any ideas on another implementation?
Think declaratively: Just consider what the predicate should describe. It's a relation between a score, a list of titles and scores and a list of titles matching the score in the first argument. I find it helpful to find descriptive names for predicates, where one can see at once which argument is what. So why not go with something like score_list_titles/3. Then I think about what cases there are to cover in my description. For this relation I see three cases:
score_list_titles(_S,[],[]). % 1: if list is empty titles is empty
score_list_titles(S,[[T,S]|TSs],[T|Ts]) :- % 2: if score matches S, T is in titles
score_list_titles(S,TSs,Ts). % relation must hold for the tails as well
score_list_titles(S,[[T,X]|TSs],Ts) :- % 3: if score
dif(S,X), % doesn't match S, T is not in titles
score_list_titles(S,TSs,Ts). % relation must hold for the tails as well
Querying this predicate produces the following results: Which titles have a score of 100, 200 and 300 respectively?
?- score_list_titles(100,[['title1',100],['title2',200],['title3',100]],T).
T = [title1,title3] ? ;
no
?- score_list_titles(200,[['title1',100],['title2',200],['title3',100]],T).
T = [title2] ? ;
no
?- score_list_titles(300,[['title1',100],['title2',200],['title3',100]],T).
T = []
What titles are there for what scores?
?- score_list_titles(S,[['title1',100],['title2',200],['title3',100]],T).
S = 100,
T = [title1,title3] ? ;
S = 200,
T = [title2] ? ;
T = [],
dif(S,100),
dif(S,200),
dif(S,100)
You can even express this relation more compactly by using if_/3:
score_list_titles(_S,[],[]).
score_list_titles(S,[[T,X]|TSs],TL) :- % if S=X: T is in titles
if_(S=X, TL=[T|Ts], TL=Ts), % otherwise it isn't
score_list_titles(S,TSs,Ts). % relation must hold for the tails as well
This way the predicate doesn't even leave an unnecessary choice-point open in the case where the first two arguments are ground (= contain no free variables). You can see that if you compare the below queries with the above ones: In the above version of score_list_titles/3 I had to enter ; after the single answer, to get the feedback that there are no further solutions.
?- score_list_titles(100,[['title1',100],['title2',200],['title3',100]],T).
T = [title1,title3]
?- score_list_titles(200,[['title1',100],['title2',200],['title3',100]],T).
T = [title2]
you're doing more complex than needed:
return_title(Score,List,X):-
member([X,Score],List).
then you can use Prolog 'all solutions' builtins, like findall/3, or REPL backtracking (hit ';' after an answer).

Prolog list not printing all the elements on console

I am using SWI-PROLOG version 6.6.6
I want to print all the attributes of a particular predicate type.
I have a predicate called law with arity 2.
Some of the facts are
law(borrow,'To borrow Money on the credit of the United States').
law(commerce,'To regulate Commerce with foreign Nations, and among the several States, and with the Indian Tribes').
law(unifomity,'To establish an uniform Rule of Naturalization, and uniform Laws on the subject of Bankruptcies throughout the United States').
law(money,'To coin Money, regulate the Value thereof, and of foreign Coin, and fix the Standard of Weights and Measures').
law(punishment,'To provide for the Punishment of counterfeiting the Securities and current Coin of the United States').
law(establishment,'To establish Post Offices and post Roads').
law(exclusiverights,'To promote the Progress of Science and useful Arts, by securing for limited Times to Authors and Inventors the exclusive Right to their respective Writings and Discoveries').
law(court,'To constitute Tribunals inferior to the supreme Court').
etc.
Now I want to access a law by entering its type.
Such as,
power(X) :- law(X,Y), display('\nCongress has the power : '),display(Y).
powers(ALL) :- display('\nCongress has the powers : '), law(_,Y), display('\n'), display(Y).
This works perfectly. Now, I also want the user to know what all types of laws are there so that the user can enter it as a query to get the corresponding law.
ex power(money).
For this, I made a query to get all these keywords and add them to a list and display the list.
But the list that is finally printed is not complete.
powerList(L) :- findall(X,law(X,_), L).
I use this code to get the list.
But the output on the console is
L = [borrow, commerce, unifomity, money, punishment, establishment, exclusiverights, court, piracyfelony|...].
But, there are more law types even after piracyfelony and they are not getting printed to the console. How do I get them printed?
This is a feature of Prolog's toplevel loops that tries to keep the output short.
To find out how you might change it, ask which Prolog flags your Prolog supports that have a value being a list of at least two elements:
?- current_prolog_flag(F,Options), Options = [_,_|_].
F = debugger_print_options,
Options = [quoted(true), portray(true), max_depth(10), attributes(portray), spacing(next_argument)] ;
F = toplevel_print_options,
Options = [quoted(true), portray(true), max_depth(10), spacing(next_argument)] ;
F = argv,
Options = [swipl, '-f', none] ;
false.
Now modify it accordingly:
?- length(L,10).
L = [_G303, _G306, _G309, _G312, _G315, _G318, _G321, _G324, _G327|...].
?- set_prolog_flag(toplevel_print_options,[quoted(true), portray(true), max_depth(0), spacing(next_argument)]).
true.
?- length(L,10).
L = [_G303, _G306, _G309, _G312, _G315, _G318, _G321, _G324, _G327, _G330].
(In newer versions starting with SWI 7 there is another flag value, answer_write_options.)

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 Sum of All Instances in a List of Facts

I have a list of facts. Each fact defines a relationship between two subjects and the number of projects they've completed. They're defined like this:
label(allGroups,[group(a,b,10),group(b,c,3),group(c,d,12)]).
I'm trying to write a function that will make a list of all the projects completed by an individual. For example, 'b' has completed a total of 13 projects while c has completed a total of 15 projects.
This is the function I've got going right now.
individualSum([],_,0).
individualSum([group(Name,_,Projects)|Tail],Name,Sum) :-
individualSum(Tail,Name,Tailsum),
Sum is Projects + Tailsum.
individualSum([group(_,Name,Projects)|Tail],Name,Sum) :-
individualSum(Tail,Name,Tailsum),
Sum is Projects + Tailsum.
I keep getting false and can't figure out if that's due to an incomplete basecase for the recursion or something else entirely. Here's what I'm running:
?- [groupSum].
?- label(allGroups,L),Groups=L).
(spits out allGroups, then:)
?- individualSum($Groups,b,Total).
false.
Any idea where I'm going wrong? I appreciate any help I can get.
You need to add another clause to skip groups here neither of the subjects is the one you are looking for:
individualSum([group(Name1,Name2,Projects)|Tail],Name,Sum):-
Name \= Name1,
Name \= Name2,
individualSum(Tail,Name,Sum).
Gusbro already spotted the problem (+1). Since you're using SWI-Prolog, you could do with an aggregation builtin.
individualSum(L,Name,Sum) :-
aggregate_all(sum(Projects),
( member(group(A,B,Projects), L), ( A = Name ; B = Name )), Sum).

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