Prolog display the results in an array/list - prolog

So I've this problem that is bugging me a lot:
We've the fact student(sID,LR,[courses]).
In which sID is the student ID, LR to determine if he is a left or right-handed person, and [courses] is an array of courses.
Next, we've the predicate listStudents(C,L) which C represents one Course and L the List. The predicate wants to return a List of Students that are in C Course.
Example:
student(23,1,[math101,phy203]).
student(24,0,[math101,phy203,art]).
student(25,1,[phy203,art]).
?- listStudents(math101,L)
L=[23,24]
Now, I've tried for several things like:
listStudents(C,L):- student(C,_,Y), member(C,Y)
and I realized later it's wrong as it returns me
L=23
L=24
Afterwards I was reading about findall() and made this
listStudents(C,L):- findall(S, study(C,S), L).
study(C,S):- estudiante(S,_,Y), member(C,Y).
which makes a return that I did not expect
L=[23,23,24,24,24]

Related

Having trouble with Prolog

I am trying to implement a Prolog task in order to learn it:
num_cred(X,Y): this function should take a list of course
abbreviations as input (X), and output the number of credits
obtained when a student attended these courses (Y).
courses(X,Y): this function should take a student as input (X), and
output a list of all courses this student attended (Y).
pass2(X): this function should take a student as input, and output
true if the student has 30 or more ECTS based on num_cred(X).
fail2(X): this function should take a student as input, and output
true if the student has less than 30 ECTS based on num_cred(X).
Terminal input-output pairs examples:
Input Output
num_cred([pai,qf,d],X). 30
courses(alice,X). [pai,qf,d]
pass2(X). alice
fail2(X). bob
This is what I've done until now:
num_cred([], 0).
num_cred([H|T], Sum) :- num_cred(T, Rest), course(H, P, C), Sum is C + Rest.
courses(X,Y).
pass2(X).
fail2(X).
/*Level 2 tests*/
course(pai,inf,10).
course(qf,eco,10).
course(d,law,10).
student(alice,ss).
attend(alice,pai).
attend(alice,qf).
attend(alice,d).
student(bob,ss).
attend(bob,pai).
Do you have any idea why my num_cred rule don't work? When I test it with num_cred([qf,pai],0). I get: Arguments are not sufficiently instantiated.
Any help/ hints with these rules will be much appreciated.
Thanks a lot.

Prolog compound statement returning multiple variables

I'm super new to Prolog, like, my professor assigned us a program and just asked us to watch a couple youtube videos. No lecture.
So anyway, here's the issue:
I'm supposed to create a pharmacist software that looks up drug interactions.
When I enter a specific drug, then Drug-variable, and Interaction-variable, I get the first drug and interaction in the list (of like, 100 drugs that interact with temazepam):
?- interacts(temazepam,Drug,Interaction).
Drug = thalidomide,
Interaction = neutropenia .
Part 1) How can I get every drug and its interaction from, say, temazepam?
Partial program listed below [because I have 1609 lines of drug interactions listed]:
interacts(X,Y,Interaction):-
Drug(X),
Drug(Y),
Interaction.
Interaction:-
Drug(X),
Drug(Y).
interacts(gatifloxacin,zolpidem,attempted_suicide).
interacts(gatifloxacin,zolpidem,insomnia).
interacts(gatifloxacin,warfarin,cardiac_decompensation).
interacts(gatifloxacin,isosorbide-5-mononitrate,arteriosclerotic_heart_disease).
interacts(gatifloxacin,rosiglitazone,hyperglycaemia).
interacts(gatifloxacin,bortezomib,hyperglycaemia).
interacts(gatifloxacin,mometasone,asthma).
interacts(gatifloxacin,cisplatin,hyperglycaemia).
interacts(gatifloxacin,cisplatin,bone_marrow_failure).
interacts(gatifloxacin,montelukast,difficulty_breathing).
interacts(temazepam,thalidomide,neutropenia).
interacts(temazepam,thalidomide,thrombocytopenia).
interacts(temazepam,timolol,drowsiness).
interacts(temazepam,tizanidine,acid_reflux).
interacts(temazepam,tizanidine,heart_attack).
interacts(temazepam,tolterodine,amnesia).
Part 2) I need to be able to list an interaction and get back every drug that caused it.
I guess just the side-effect then all drug interactions listed would be better than listing drug1+sideEffect = drug2.
Example:
?- interacts(Drug,Drug,amnesia).
Part 3) I should be able to enter a single drug, and get everything with interactions and everything with no interactions.
Example:
?- interacts(valacyclovir,Drug,Interaction).
Drug = zolpidem,
Interaction = anxiety
But for everything
Excuse me for the edits!
Thanks so much in advance!
You can use the built-in predicate findall/3 for that:
drug_allinteractions(Drug,AI) :-
findall((D,I),interacts(Drug,D,I),AI).
The only goal of drug_allinteractions/2 is using findall/3 to query interacts/3 and put its second and third argument into the list AI as a tuple (D,I). Example query: Wich interacting drugs with what interaction-effect are known for gatifloxacin?:
?- drug_allinteractions(gatifloxacin,L).
L = [(zolpidem,attempted_suicide),(zolpidem,insomnia),(warfarin,cardiac_decompensation),(isosorbide-5-mononitrate,arteriosclerotic_heart_disease),(rosiglitazone,hyperglycaemia),(bortezomib,hyperglycaemia),(mometasone,asthma),(cisplatin,hyperglycaemia),(cisplatin,bone_marrow_failure),(montelukast,difficulty_breathing)]
Alternatively, if you just want to query interacts/3 and not write a program:
?- findall((D,I),interacts(gatifloxacin,D,I),AI).
AI = [(zolpidem,attempted_suicide),(zolpidem,insomnia),(warfarin,cardiac_decompensation),(isosorbide-5-mononitrate,arteriosclerotic_heart_disease),(rosiglitazone,hyperglycaemia),(bortezomib,hyperglycaemia),(mometasone,asthma),(cisplatin,hyperglycaemia),(cisplatin,bone_marrow_failure),(montelukast,difficulty_breathing)]
As for your added part 2): You can use findall on your original query:
?- findall((D1,D2),interacts(D1,D2,amnesia),AI).
AI = [(temazepam,tolterodine)]
Note, that unlike in your example I wrote two different variables D1 and D2 for the drugs, otherwise you are asking which drug has the interaction-effect amnesia with itself.
Considering your added part 3) I'm not entirely sure what you want. Your query reads: "Show me all drugs that interact with valacyclovir plus the associated effect". That is basically the same as your very first query, just for a different drug. You can query for all drugs in the relation interacts/3 interactively without showing the interacting drugs and the effects by:
?- interacts(D,_,_).
D = gatifloxacin ? ;
...
Or query for an entire list without duplicates by using setof/3:
?- setof(D1,D2^I^interacts(D1,D2,I),AI).
AI = [gatifloxacin,temazepam]
If you, however, try to find a list of drugs that are not interacting with a given drug, you can write a predicate, say drug_noninteractingdrug/2...
:- use_module(library(lists)).
drug_noninteractingdrug(D,NID) :-
dif(D,NID), % D and NID are different
setof(D2,D^interacts(D,D2,_),L), % L ... all drugs interacting with D
interacts(NID,_,_), % NID ... drug...
\+member(NID,L). % ... that is not in L
... and query this using setof/3:
?- setof(NID,drug_noninteractingdrug(gatifloxacin,NID),NIDs).
NIDs = [temazepam]
With your given minimal example this query of course only yields one drug. Note that you need to include library(lists) for the predicate member/2.

Finding all solutions in prolog

In prolog I'm trying to unify every valid pairing of needs with resources
needs([ece2090,1,m,13,16]).
needs([ece3520,1,tu,11,14]).
needs([ece4420,1,w,13,16]).
resources([joel, [ece2090,ece2010,ece3520,ece4420],[[m,13,16]]]).
resources([sam, [ece2010,ece4420],[]]).
resources([pete, [ece3520],[[w,13,16]]]).
using this formula
make_bid([Class,Sect,Day,Ts,Te],[Name,Cap,Unavail],[Class,Sect,Day,Ts,Te,Name,_]) :-
no_conflict_all_unavailable(Day,Ts,Te,Unavail),
course_capable(Class,Cap),
writef('%w %w %w\n',[Class,Sect,Name]),
fail.
and running this test.
test(Listing) :- needs(N), resources(R), make_bid(N,R,Listing).
The point of this part of the program is to pair every class with a teacher that both has the qualifications to teach the class and is not unavailable during that time. It's supposed to give a list.
?- test(Listing).
ece3520 1 joel
ece3520 1 pete
ece4420 1 joel
ece4420 1 sam
false.
When run, the above is generated. This is correct, but it's in a format that's useless to me, since I need it to be a variable of its own to do further computations. Then the solution is to use bagof or findall, right?
So I remove the fail clause from the main part of the program and then change the test to this
test(Bag) :- needs(N), resources(R), bagof(Listing,make_bid(N,R,Listing),Bag).
but it generates this
ece3520 1 joel
Bag = [[ece3520, 1, tu, 11, 14, joel, _G4310]]
If you look closely you'll see that there's no period at the end as well as a lack of a true/false statement. This would lead one to believe it is infinitely looping. This isn't the case however, as the Bag matrix is fully formed and I can simply type "." to end the program (instead of, you know, aborting it).
It only generates the first valid solution. Why is this happening?
You've structured your test predicate so that bagof/3 is called for every instance combination of needs(N) and resources(R) and so it collects each result of make_bid in it's own bagof/3 result:
ece3520 1 joel
Bag = [[ece3520, 1, tu, 11, 14, joel, _G4310]]
The first line is the write that is in make_bid predicate. The second line is the Bag result for the single query to make_bid for one pair of needs/resources. The last argument in the list, _G4310, occurs because your predicate uses _ and it's anonymous (never used/instantiated).
Your current make_bid is designed to write the results in a loop rather than instantiate them in multiple backtracks. So that could be changed to:
make_bid([Class, Sect, Day, Ts, Te], [Name, Cap, Unavail], [Class, Sect, Day, Ts, Te, Name, _]) :-
no_conflict_all_unavailable(Day, Ts, Te, Unavail),
course_capable(Class, Cap).
(NOTE: I'm not sure why you have _ at the end of the 3rd list argument. What does it represent?)
If you want to collect the whole result in one list, then you canb use findall/3:
findall([Class, Sect, Name], (needs(N), resources(R), make_bid(N, R, [Class, Sect, _, _, _, Name, _]), Listings).
This will collect a list of elements that look like, [Class, Sect, Name]. You could use bagof/3 here, but you'd need an existential quantifier for the variables in the make_bid/3 call that you don't want to bind.
If you wanted the entire Listing list, then:
findall(L, (needs(N), resources(R), make_bid(N, R, L)), Listings).
But each element of Listings will be a list whose last element is an anonymous variable, since that's how make_bid/3 is structured.

setof/3 and NAF

so I have a set of facts:
course(cs61, "intro to cs")
...
course(cs62b, "data structure")
...
grade(adam, cs61, spring11, "A")
...
grade(chrisitian, cs61, fall2010, "A-")
I need to define a predicate good_standing(Student) to determine whether the Student got A in every class he took. I must use 2 different approaches:
use setof/3
use NAF
on the 1st. I have tried to figure out get Result_list: the set of students that got A from every class he took. and then call member(Student, Result_list). But I don't know what to get the Result_list since the syntax is a bit strange to me.
1) For the NAF solution you should keep in mind that the good-standing student is the one with no grades lower than A, i.e., the one such that there is no course he/she took and the grade is lower than A. We also require this person to take at least one course.
good_standing(Student) :- grade(Student, _,_,_),
\+ (grade(Student, Course, _, Grade),
lower(Grade,"A")).
with lower/2 defining relation between the grades:
lower("A-","A").
lower("B","A").
...
The solution above works if students get only one grade per course.
2) For the setof solution you can find all grades per student and check that they are A.
grade(Student, Grade) :- grade(Student, _,_,Grade).
good_standing(Student) :- setof(Grade, grade(Student,Grade), ["A"]).
Alternatively, as suggested by false we can make the existential quantification explicit within setof:
good_standing(Student) :- setof(Grade,
Course^Period^grade(Student,Course,Period,Grade),
["A"]).

Returning a list in prolog

i wanna ask a question about returning a list...
Facts:
TEAM(TEAMNAME,DIRECTOR,NATIOANALITY,OVERALLGOAL)
team (milan,allegri,italy, 8.5).
team (inter,benitez,italy,7.6).
team (barcelona,guardiola,spain,7.8).
team (realmadrid,mourinho,spain,7.2).
and i want to create a predicate:
find(T,N,G) : T is name of team, N is nationality of team and this team's overallgoal must be greater than G. and outputs must be like these:
find([], spain,9). returns true
find(X, spain,6). returns X=[barcelona, realmadrid]
i tried to do this with:
find(T,N,G):-find1(T,N,G),is_set(T).
find1([]).
find1([T|Ts],N,G):-team(T,_,N,Gs),Gc>G,find1(Ts).
it gives results but not like output above...
if my goal is find([],spain,9). then give false...
if my goal is find(X,spain,6). then give first X=barcelona and wait for ";" after that give X=realmadrid... but i want to a list like above...
Thanks a lot...
To extract a list of items satisfying a predicate from a database of clauses, one should use findall predicate. For example, your code could be rewritten as follows:
find(T, N, G) :- findall(X, (team(X, _, N, G0), G0 > G), T).

Resources