Prolog compound statement returning multiple variables - prolog

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.

Related

How to obtain a list given this database

I am learning prolog and currently stuck on this exercise. I am trying to obtain a list of Ontario teams that outputs:
From the database below.
city(ottawa,ontario).
city(guelph,ontario).
city(kingston,ontario).
city(gatineau,quebec).
city(montreal,quebec).
team(ravens,ottawa).
team(ggs,ottawa).
team(gryphons,guelph).
team(queens,kingston).
team(torrents,gatineau).
team(stingers,montreal).
Here's what I have so far.
setof(X, city(X,ontario), L). %This returns L = [guelph,kingston,ottawa]
How would I go about the next step to obtain
L = [ggs, gryphons, queens, ravens].
source
city(ottawa,ontario).
city(guelph,ontario).
city(kingston,ontario).
city(gatineau,quebec).
city(montreal,quebec).
team(ravens,ottawa).
team(ggs,ottawa).
team(gryphons,guelph).
team(queens,kingston).
team(torrents,gatineau).
team(stingers,montreal).
city_team(_city_,_team_) :-
city(_city_,_province_) ,
team(_team_,_city_) .
demo
?- setof(_team_,city_team(_,_team_),_teams_) , T = _teams_ .
T = [torrents] ? ;
T = [gryphons] ? ;
T = [queens] ? ;
T = [stingers] ? ;
T = [ggs,ravens]
The ISO Prolog standard specifies three meta-predicates that collect solutions for a goal: bagof/3, setof/3, and findall/3 (there's also a de facto standard findall/4 meta-predicate). These meta-predicates have different semantics, notably on how they deal with variables on the goal argument (the second argument) that are not in the template argument (the first argument). All of them are described as meta-predicates as they take a goal as one of the arguments. Let's illustrate their semantics using your city/2 predicate.
First, we try bagof/3:
| ?- bagof(City, city(City, State), Solutions).
Solutions = [ottawa,guelph,kingston]
State = ontario ? ;
Solutions = [gatineau,montreal]
State = quebec
yes
Notice that the solutions are aggregated by state, which is a variable that occurs in the goal but not in the template. Let's try it now with setof/3:
| ?- setof(City, city(City, State), Solutions).
Solutions = [guelph,kingston,ottawa]
State = ontario ? ;
Solutions = [gatineau,montreal]
State = quebec
yes
Notice the difference in the first solution? They are ordered. The setof/3 predicate sorts solutions in standard order (using term comparison) and also eliminates duplicates.
The meta-predicate findall/3, however, doesn't aggregate solutions for the bindings of variables in the goal not occurring in the template:
| ?- findall(City, city(City, State), Solutions).
Solutions = [ottawa,guelph,kingston,gatineau,montreal]
yes
Therefore, you can use it to answer your question. The following query returns all teams:
| ?- findall(Team, team(Team, City), Teams).
Teams = [ravens,ggs,gryphons,queens,torrents,stingers]
yes
To get only the teams in the state of Ontario we can use:
| ?- findall(Team, (team(Team, City), city(City,ontario)), Teams).
Teams = [ravens,ggs,gryphons,queens]
yes
We can also get the list ordered by using setof/3:
| ?- setof(Team, City^(team(Team, City), city(City,ontario)), Teams).
Teams = [ggs,gryphons,queens,ravens]
yes
The City^ construct instructs setof/3 (can also be used with bagof/3) to not aggregate solutions by bindings for the City variable.
More can be written about these predicates, notably about variable terminology and the ^/2 operator but this answer is getting long. One final remark, however: In general, for clarity and best performance, avoid using complex goal arguments with these meta-predicates. Is often clear to define an auxiliary predicate. For example:
state_team(State, Team) :-
team(Team, City),
city(City, State).
Using this predicate:
| ?- setof(Team, state_team(ontario,Team), Teams).
Teams = [ggs,gryphons,queens,ravens]
yes

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 bagof, setof, findall predicates

How do I query database facts with 3 or more attributes in Prolog using bagof, setof. An example I have a defined a database students(name, grade,sport,gender). I want find a list of students that play a particular sport, say cricket. My current query
sport_list(L):-
bagof(S,N^G^D^students(N,G,S,D),L),
S = cricket.
student(patash,5,rugby,male).
student(naomi,3,netball,female).
student(lepo,6,_,male).
student(diamal,4,cricket,male).
student(bonga,5,chess,female).
student(imi,6,cricket,male).
student(ayanda,3,_,female).
You could model your knowledge base such that the third argument is none for unathletic students instead of _:
student(lepo,6,none,male).
student(ayanda,3,none,female).
Then you can define a predicate that describes atheletic students as being those who do not have none as sport:
athletic(S) :-
dif(X,none),
student(S,_,X,_).
Subsequently use athletic/1 in the single goal of sport_list/1:
sport_list(L):-
bagof(S,athletic(S),L).
That yields the desired result:
?- sport_list(L).
L = [patash,naomi,diamal,bonga,imi]

Prolog check for predicate category

I have been working with Prolog since today, and wanted to create a simple test case. The basic idea was to have multiple sports defined, and it looks as follows:
soccer :- category(ball_sport),
check(has_11_players_per_team),
check(large_ball),
check(use_feet).
tennis :- category(ball_sport),
...
category(ball_sport) :-
check(has_a_ball).
Now I wanted to create a testcase, to see if both sports are of the ball_sport category, but have no idea to check these sports against eachother .. I thought it would be something like the code below, but it's obvious not. Is there an easy way to check these predicate categories? Thanks
both_ballsports(sport_one, sport_two) :-
has_category(sport_one, ball_sport),
has_category_sport_two, ball_sport).
It seems that first of all, you want to declaratively state attributes of a sport.
For example:
sport_attributes(soccer, [ball_sport,players(22),ball(large),use(feet)]).
sport_attributes(tennis, [ball_sport,players(2),players(4),ball(small),use(racket)]).
Note that I am relating sports to attributes. For comparison, the goals of the form check(X) you use above all seem to lack a critical argument, namely the actual sport for which they hold (or not). For example, the goal check(use_feet) either holds or not, but there is no means to qualify a unary predicate of this kind and state different facts for different sports.
Note the naming convention: We describe what each argument means, separated by underscores.
With this representation, both_ballsports/2 could look like this:
both_ballsports(Sport1, Sport2) :-
ballsport(Sport1),
ballsport(Sport2).
ballsport(Sport) :-
sport_attributes(Sport, As),
member(ball(_), As).
Sample query and answer:
?- both_ballsports(Sport1, Sport2).
Sport1 = Sport2, Sport2 = soccer ;
Sport1 = soccer,
Sport2 = tennis ;
Sport1 = tennis,
Sport2 = soccer ;
Sport1 = Sport2, Sport2 = tennis ;
false.
This can be used in all directions!

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

Resources