Prolog check for predicate category - prolog

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!

Related

Prolog, Outputting a Variable After a Relation is Used

This is my first-day learning prolog and I would like to write a program that decides what shoes I should wear based on the weather and what kind of appointment I have at the office. The main "function" would be declared for example:
"go :- outfit(snow, 15, casual, F1), write(F1)."
Where snow is the weather, 15 is the temperature(not relevant now), and casual is the formality of the appointment. "write(F1)" will display the "output" so the variable F1 needs to be the result of said relation(s). Here are the rules for what shoes to wear:
%Rules for weather
rain(Weather) :- (Weather = 'rain').
snow(Weather) :- (Weather = 'snow').
nice(Weather) :- (Weather = 'nice').
clear(Weather) :- (Weather = 'clear').
%Rules for formality
formal(Appointment) :- (Appointment = 'formal').
semiformal(Appointment) :- (Appointment = 'semiformal').
casual(Appointment) :- (Appointment = 'casual').
%Rules for when to wear a type of footwear
dressShoes(Appointment) :- formal(Appointment).
boots(Appointment, Weather) :- not(formal(Appointment)), (rain(Weather);snow(Weather)).
sneakers(Appointment, Weather) :- not(formal(Appointment)), (nice(Weather);clear(Weather)).
This is where my issue is, I am not sure how to tie the last three relations to a single relation that fills the variable "F1" for my final "outfit" function. I am a C++ guy, so I would like to essentially place a string into F1 like "[sneakers]" or "[boots]" but this is part of my growing pains with prolog. Any help is much appreciated.
Some misunderstandings about Prolog I guess. This kind of rule:
rule(Variable) :- (Variable = 'value').
You don't need to quote 'value', it is already an atom. Whatever book you are reading, look up atoms. It becomes:
rule(Variable) :- (Variable = value).
You don't need the extra parentheses in the rule definition. It becomes:
rule(Variable) :- Variable = value.
You don't need the explicit unification in the body. There is nothing else happening between the head and the body. So you don't need a variable, either. It becomes:
rule(value).
Applying this to your program, I get:
rain(rain).
snow(snow).
nice(nice).
clear(clear).
%Rules for formality
formal(formal).
semiformal(semiformal).
casual(casual).
Those rules say pretty much nothing ;-)
Your example at the very top:
go :- outfit(snow, 15, casual, F1), write(F1).
Does exactly the same as just calling outfit(snow, 15, casual, F1). So what is the purpose of the "go" and the "write"? Skip them I guess.
The logic of your program: can you explain it without code? Your code is so unusual that I have to guess.
If you want to say, "If the appointment is formal, put on dress shoes", you could write it like this:
occasion_shoes(Occasion, Shoes) :-
formality_occasion(Formality, Occasion),
formality_shoes(Formality, Shoes).
formality_occasion(formal, evening_party).
formality_occasion(semi_formal, office).
formality_shoes(formal, dress_shoes).
Do you see what is going on? You match the occasion to the shoes. To do that, you look up the formality of the occasion in the table formality_occasion/2 and then match it to the formality of the shoes in the formality_shoes/2 table.
If you are struggling to model your problem, you can also read up on relational database design.

Prolog: How would I seperate a list into two lists depending on the category?

My query would be the following:
separate([eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],food,drink).
food = [eat(chips),eat(burger),eat(banana)]
drink = [drink(water),drink(coke)]
I want to separate the list but I've not been able to figure out how to.
separate(X,Cat1,Cat2):-
[Cat1|Cat2] = X,
Cat2 = X,
separate(X,Cat1,Cat2).
Currently I've been only able to use recursion to go through each element of the list but I don't really have any idea on how to start separating them into separate lists.
You could use the higher-order filter operations taking a Goal for filtering:
include/3
exclude/3
For example (note that variables have to start with uppercase letters in Prolog):
separate(TaggedList,Food,Drink) :-
include(isFood,TaggedList,Food), % isFood/1 will be called for each element
include(isDrink,TaggedList,Drink). % same as above
isFood(eat(_)). % no need to be complex; just succeed if argument matches
isDrink(drink(_)). % same as above
And so:
?- separate(
[eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],
Food,Drink).
Food = [eat(chips), eat(burger), eat(banana)],
Drink = [drink(water), drink(coke)].
Pattern matching. You want pattern matching.
When the list of food and drink is empty you have an easy base predicate:
separate([],[],[]).
When you want to separate out the food and drink it is as easy as this:
separate([eat(X)|T],[eat(X)|F],D) :- separate(T,F,D).
separate([drink(X)|T],F,[drink(X)|D]) :- separate(T,F,D).
When the head of the list matches eat then put the element on the first place of the Food list. When drink then on the Drink list. Simple.
When I run that:
?- separate([eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],Food,Drink).
Food = [eat(chips), eat(burger), eat(banana)],
Drink = [drink(water), drink(coke)].
Both answers are good as they are. Another option for SWI-Prolog would be to use partition/4 like this:
separate(List, Eats, Drinks) :-
partition(is_eat, List, Eats, Drinks).
is_eat(eat(_)).
Extra credit: why does this not work?
?- partition(eat(_),
[eat(chips),drink(water),eat(burger),eat(banana),drink(coke)],
Eats, Drinks).

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, print employees with same names

This is my first time using Prolog.
I have employees:
employee(eID,firstname,lastname,month,year).
I have units:
unit(uID,type,eId).
I want to make a predicate
double_name(X).
that prints the last names of the employees with the same first name in the unit X.
I am doing something like this :
double_name(X) :-
unit(X,_,_eID),
employee(_eID,_firstname,_,_,_),
_name = _firstname,
employee(_,_name,_lastname,_,_),
write(_lastname).
But it prints all the employees in the unit.
How can i print only the employees with the same name ?
unit(unit_01,type,1).
unit(unit_01,type,2).
unit(unit_01,type,3).
employee(1,mary,smith,6,1992).
employee(2,fred,jones,1,1990).
employee(3,mary,cobbler,2,1995).
double_name(Unit) :-
unit(Unit,_,Eid_1),
employee(Eid_1,Firstname,Lastname_1,_,_),
unit(Unit,_,Eid_2),
Eid_1 \= Eid_2,
employee(Eid_2,Firstname,Lastname_2,_,_),
write(Firstname),write(","),write(Lastname_1),nl,
write(Firstname),write(","),write(Lastname_2).
Variables in Prolog typically start with an upper case letter, but starting them with and underscore is allowed, but not typical.
In double_name/2 the predicates like
unit(Unit,_,Eid_1)
employee(Eid_1,Firstname,Lastname_1,_,_)
are used to load the values from the facts into variables while pattern matching (via unification) that the bound variables match with the fact.
To ensure that a person is not compared with themselves.
Eid_1 \= Eid_2
and to make sure that two people have the same first name the same variable is used: Firstname.
The write/1 and nl/0 predicates just write the result to the screen.
Example:
?- double_name(unit_01).
mary,smith
mary,cobbler
true ;
mary,cobbler
mary,smith
true ;
false.
Notice that the correct answer is duplicated. This can be resolved.
See: Prolog check if first element in lists are not equal and second item in list is equal
and look at the use of normalize/4 and setof/3 in my answer
which I leave as an exercise for you.

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.

Resources