Having trouble with Prolog - 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.

Related

Prolog display the results in an array/list

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]

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.

Checking List in Prolog

I'm trying to do a schedule that has constraints in Prolog. Scheduling will be based on two constraints. The courses of the same semester and the courses taught by the same instructor cannot be scheduled to the same time slot.
course(ceng123,1).
Course code and semester of course.
slot(monday,1).
Day and hour on day.
teaches(jack,ceng123).
Teacher and course code.
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
The answer that I expect is:
?- schedule([cse111,cse112,cse113,cse114,cse115,cse116,cse117,cse118],X).
X = [cse111, monday, 1, cse112, monday, 2, cse113, tuesday, 1, cse114, tuesday, 2, cse115, monday, 1, cse116, monday, 2, cse117, tuesday, 1, cse118, tuesday, 2]
I wrote a code that has no constraints:
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
schedule([],[]).
schedule([Course|CourseTail],[Course,Day,Slot|ScheduleTail]):-
slot(Day,Slot),schedule(CourseTail,ScheduleTail).
There is no problem, but when i try this;
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
schedule([],[]).
schedule([Course|CourseTail],[Course,Day,Slot|ScheduleTail]):-
schedule(CourseTail,ScheduleTail), check(Course,Day,Slot,ScheduleTail).
check(_, _, _,[]).
check(Course,Day,Slot,[Course2,Day2,Slot2|Tail]):- check(Course,Day,Slot,Tail),
course(Course,Semester1),course(Course2,Semester2),Semester1=\=Semester2,
slot(Day,Slot),slot(Day2,Slot2).
I tried to write constraint but I took an error.
uncaught exception: error(syntax_error('user_input:1 (char:4) . or operator expected after expression'),read_term/3)
Can you see the mistake?
Singleton variable is a variable which is mentioned only once in the program: See wiki. You get that for line 27 which I presume is this one: check(Course,Day,Slot,[]). You can replace it with check(_, _, _,[]). ('_' means any variable. This means you can universally quantify the variables.)
You do not get an error. Prolog saying no just means that your constraints cannot be satisfied.
You should start by clearly defining your constraints.
From your comments, I read:
Two courses in an hour cannot have same semester number and same teacher's courses will not be in same hour.
Therefore you can assign a schedule to a course when it fulfills those requirements, hinting that you have to know which assignments have been already issued. So you can create a procedure that keeps the current schedule and only add a new assignment when your requirements are met.
So, start by defining your schedule/2 procedure that calls a new procedure schedule/3 with an empty list:
schedule(Courses,Schedule):-
schedule(Courses, [], Schedule).
Now, this procedure has on the first argument the list of courses to assign a schedule, keeps the current assignments con the second argument and unifies the third argument with the final Schedule (with the data you require, that is Course, Day and Slot).
I'll use a structure to represent each assignment schedule(Course, Day, Slot) because just mixing those datum in a list is not a good idea.
schedule([], _, []).
schedule([Course|CourseTail], Courses, [schedule(Course,Day,Slot)|ScheduleTail]):-
% Two courses in an hour cannot have same semester number and same teacher's courses will not be in same hour.
course(Course, Semester),
teaches(Teacher, Course),
slot(Day, Slot),
\+ member(s(Day, Slot, Semester, _), Courses),
\+ member(s(Day, Slot, _, Teacher), Courses),
schedule(CourseTail, [s(Day, Slot, Semester, Teacher)|Courses], ScheduleTail).
First clause is the base case. It states that when there are no more courses in the input list, then the schedule will be empty.
Second clause takes the first Course from the input list and computes a possible Semester, Theacher and Day/Slot for that course, and then just checks if the constraints are met. It tests whether there is already a schedule for that Day/Slot/Semester and whether there is already an assignment for the Teacher at that Day/Slot. If the requirements are met then we add this assignment to the list of current assignments and continue recursion.
Upon return, we add the Course/Day/Slot to the list of the final Schedule.

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