Querying each element of a list - prolog

I am doing homework for AI class and I am writing a prolog program.
I am supposed to take a list of names and check if each person in the list belongs to a specific country chosen.
what i have so far
% facts
person(bruce, australia, rhodri, bronwyn).
person(rhodri, newyork, dan, mary).
person(bronwyn, miami, gar, roo).
person(dan, miami, george, mimi).
person(mary, texas, mack, tiki).
person(gar, jamaica, zid, rem).
person(roo, newzealand, john, jill).
person(tom, mayday, dick, mel).
person(dick, newyork, harry, rin).
person(mel, miami, tom, stacey).
person(harry, miami, george, mimi).
person(rin, texas, mack, tiki).
person(tom, jamaica, zid, rem).
person(stacey, newzealand, john, jill).
% rules
eligible(P,C) :-
person(P, C, F, M) , !
; person(F, C, Newfather, Newmother), !
; person(M, C, Newfather, Newmother), !
; person(Newfather, C, Grandfather , Grandmother), !
; person(Newmother, C, Grandfather, Grandmother).
checkteam([] , C).
checkteam([H|T] , C) :- eligible(H, C) , checkteam(T, C).
the last two lines in particular i am having issues with, i am trying to test each member of the list with the eligible() function then let the first element of tail become the head and repeat.
I cant figure out a way to test each member and then display a fail if any of the members are not eligible or true if all members belong to that country.
Thanks in advance.
EDIT: was fooling around and changed the code a little, as for results
?- checkteam([bruce, dan], mayday).
true.
even though neither bruce or dan are from mayday or any parents or grandparents that do.

Your eligible predicate doesn't make sense to me (probably I am misunderstanding). But, if person is defined as person(Name, Country, Father, Mother) then it could be:
eligible(Name, Country) :- person(Name, Country, _, _).
eligible(Name, Country) :- person(Name, _, Father, _),
person(Father, Country, _, _).
eligible(Name, Country) :- person(Name, _, _, Mother),
person(Mother, Country, _, _).
Then your checkteam should still give you a warning. Put an underscore at the beginning of the variable name to get rid of it:
checkteam([], _Country).

Related

Can't figure out how to get logical answer I'm looking for in Prolog

I'm trying to learn swi-prolog and I'm struggling with the following question.
The question goes: Vanessa, Mary and Penny study chemistry, biology and math, in countries Spain, Portugal, Venezuela. It is known, that Vanessa is not studying in Spain, while Mary is not in Portugal. The one who lives in Spain doesn't study math. The one in Portugal studies chemistry. Mary doesn't like biology, but likes Caracas. Define what each person studies and from where?
I tried many approaches where I tried to find where each person lives and then which subject is studied in each country, but when I try to combine those two things to get the end result, it is not definitive, and gives me ambiguous answer, meaning I get alternatives where one person lives in 2 different countries and might study 3 subjects. Any help or guidance is appreciated.
A possible solution could be the following:
At first, we add the countries, persons, and subjects to the knowledgebase:
country(spain).
country(portugal).
country(venezuela).
person(vanessa).
person(mary).
person(penny).
subject(math).
subject(biology).
subject(chemistry).
Then we add invalid combinations:
invalidPC(vanessa, spain). % vanessa is not studying in spain
invalidPC(mary, portugal). % mary is not in portugal
invalidPC(mary, spain). % mary likes caracas (venezuela)
invalidPS(mary, biology). % mary doesn't like biology
invalidCS(spain, math). % the one who lives in spain doesn't study math
invalidCS(portugal, X) :- X \= chemistry. % the one in portugal studies chemistry
Next we need a predicate to determine valid country-person-subject combinations:
student([C, P, S]) :-
country(C),
person(P),
subject(S),
\+ invalidPC(P, C),
\+ invalidPS(P, S),
\+ invalidCS(C, S).
And last a predicate which calculates three students and checks if their values are distinct:
findStudents([C1, P1, S1], [C2, P2, S2], [C3, P3, S3]) :-
student([C1, P1, S1]),
student([C2, P2, S2]),
student([C3, P3, S3]),
distinct(C1, C2, C3),
distinct(P1, P2, P3),
distinct(S1, S2, S3),
!. % stop after the first solution to prevent permutations of the student order
distinct(A, B, C) :-
A \= B,
A \= C,
B \= C, !.
This returns one result (without the ! in findStudents/3 there are 6 results, but these are only permutations).
You can also omit the ! and for example set P1, P2, and P3 to fix values, i.e. vanessa, mary, penny.

How to list all the trips discarding one airline

This is my flights data
#flight(city1, city2,airline,distance,time,price).
flight(dublin, london,ab,8000,4,1000).
flight(moscow,london,ab,9000,5,2000).
flight(dublin,moscow,bc,1000,6,3000).
flight(cork, moscow,ca,2000,7,4000).
flight(chicago, dublin,ab,6000,8,4500).
flight(berlin, moscow,bc,3000,9,4600).
flight(cork, newyork,ca,4000,10,4700).
flight(paris, hongkong,bc,11000,11,4890).
connected(X,Y,_,_,_,_) :- flight(X,Y,_,_,_,_) ; flight(Y,X,_,_,_,_).
I have to get all the trips between city1 and city2 discarding one airline
I have calculated the trip something like
trips(A,B,Path) :-
traverse(A,B,[A],Q),
reverse(Q,Path).
traverse(A,B,P,[B|P]) :-
connected(A,B,_,_,_,_).
traverse(A,B,Visited,Path) :-
connected(A,C,_,_,_,_),
C \== B,
\+member(C,Visited),
traverse(C,B,[C|Visited],Path).
This is the all_trip
Alltrip(C,L,T):-
findall(Ci, trips(C,L,Ci), T).
I have to compute this
Alltrip_noairline(X,Y,T,A):- where X and Y are city ,T is the list of all trips and DISCARD all the trip containing a flight with airline A
I am stuck here ,don't know how to start ,any help would be appreciated .Thanks
I believe connected/6 is in fact flight/6
You may add a Filter goal to your procedures:
trips(A,B,Filter, Path) :-
traverse(A,B,Filter, [A],Q),
reverse(Q,Path).
traverse(A,B, Filter, P,[B|P]) :-
flight(A,B,Airline,_,_,_),
call(Filter, Airline).
traverse(A,B, Filter, Visited,Path) :-
flight(A,C,Airline,_,_,_),
C \== B,
call(Filter, Airline),
\+ member(C,Visited),
traverse(C,B, Filter, [C|Visited],Path).
Then in alltrip/3 use a filter that accepts any airline:
alltrip(C,L,T):-
findall(Ci, trips(C,L, any, Ci), T).
any(_).
and in alltrip_noairline/4 one that forbids certain airline:
alltrip_noairline(C,L,T, A):-
findall(Ci, trips(C,L, except(A), Ci), T).
except(X, Y):- X\=Y.

Usage of distinct still leading to duplicates

I'm new to prolog, and as I understand it, the purpose of 'distinct' is to weed out duplicates. However, this code block:
allpartsincity(City):-
distinct((proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
part(Part, Name, Color, Num, X),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.
yields the following:
?- allpartsincity(london).
p2 bolt green 17 paris
p5 cam blue 12 paris
p2 bolt green 17 paris
p6 cog red 19 london
p5 cam blue 12 paris
true.
I'm not sure what I'm missing, but I'd appreciate if someone could point me in the right direction.
distinct/1 is a quite new predicate. It is only of relevance, if the incremental evaluation is of importance either because of infinite data or because (for some obscure reason) the exact order of answers is of relevance. OK, and maybe also because there are many redundant answers and the space to store them would be forbidding, but then a good setof/3 implementation might use a similar technique as well. In your case, you have just a data base of finitely many facts.
Since 19821, the classic predicate for your purpose is setof/3.
You did not give a minimal reproducible example. So I need to do some guessing. In any case, do trust the prolog-toplevel for printing.
city_part(City, CPart) :-
setof(t, city_part0(City, CPart), _).
city_part0(City, part(Part, Name, Color, Num, X)) :-
proj(Project, _A1, City),
sppj(_A2, Part, Project, _A3),
part(Part, Name, Color, Num, X).
You can avoid the intermediary predicate, but then the variable quantification will become cumbersome. I have given these variables already the names A1, A2, A3. These plus Project are only internal variables.
city_part(City, CPart) :-
setof(t, A1^A2^A3^Project^
( CPart = part(Part, Name, Color, Num, X),
proj(Project, A1, City),
sppj(A2, Part, Project, A3),
part(Part, Name, Color, Num, X)
), _).
As you wrote it, the goal part/5 that provides displayed values is unrelated to the conjunction you asked for in distinct/1. If I understand your problem correctly, most likely you should use distinct/2 instead. Try for instance
allpartsincity(City):-
distinct(part(Part, Name, Color, Num, X), (proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.

How to write rules to list all city when asked in query?

How to set the rules and when ask the query to list all the cities it works?
/*CITY DATABASE */
capital(bern).
capital(london).
capital(prague).
capital(bonn).
capital(belgrade).
city_in(prague,czechoslovakia).
city_in(berlin,germany).
city_in(hamburg,germany).
city_in(belgrade,yugoslavia).
city_in(bern,switzerland).
city_in(london,united_kingdom).
belongs_to(czechoslovakia,’COMECON’).
belongs_to(germany,’EC’).
belongs_to(switzerland,’EFTA’).
belongs_to(united_kingdom,’EC’).
/*END*/
To get the full list of cities available with this ruleset, the following query will list all of your cities in the X variable (tested in SWI-Prolog):
city_in(X, _).
To convert the cities into a single list, use findall/3 (Xs will have the result):
findall(X, city_in(X, _), Xs).
If you're trying to get the list of cities which belong to some organisation, you can issue the following and get both a list of countries and cities which exist in the organisation:
belongs_to(Country, _), city_in(X, Country).
Again, for a list of responses with findall/3:
findall(X, (belongs_to(Country, _), city_in(X, Country)), Xs).
A list of capital cities which belong to some organisation (listed in the City variable):
belongs_to(Country, _), city_in(City, Country), capital(City).
And, for a list of cities belonging to an organisation with findall/3:
findall(City, (belongs_to(Country, _), city_in(City, Country), capital(City)), Cities).
Output of the above:
?- findall(City, (belongs_to(Country, _), city_in(City, Country), capital(City)), Cities).
Cities = [prague, bern, london].

How to make fact(any) in Prolog?

I have database which consists of list of trees and facts about those trees. For example:
softness(soft).
softness(hard).
softness(veryhard).
color(gray_brown).
color(soft_red).
color(light).
color(dark).
wood(oak, leafes(leafed), softness(hard), color(gray_brown), on_touch(smalltexture)).
And I'm trying to make rule which will ask user input on specific parameters of tree and then seek for appropriate one. Like this.
what_wood(A, B):-
wood(A, B, _, _, _);
wood(A, _, B, _, _);
wood(A, _, _, B, _);
wood(A, _, _, _, B);
wood(A, B, _, _); %I have one tree with three parameters =/
wood(A, _, B, _);
wood(A, _, _, B).
what_wood(A) :-
write('Leafes: '), read(X), what_wood(A, leafes(X)),
write('Softness: '), read(Y), what_wood(A, softness(Y)),
write('Color: '), read(Z), what_wood(A, color(Z)),
write('On touch: '), read(Q), what_wood(A, on_touch(Q)).
So my question - if user wants to specify parameter as "any" is there a way to do something like this?
leafes(leafed).
leafes(coniferous).
leafes(any):-
leafes(X). %this one doesn't work. Prints false
%leafes(leafed);leafes(coniferous). %Of course this doesn't work too.
(Sorry for my English :) )
=====UPDATE=====
I ended up with this code which works fine thanks to you :)
Will add check for user input also.
wood(oak, leafed).
wood(oak, hard).
wood(oak, gray_brown).
wood(oak, smalltexture).
wood(beech, leafed).
wood(beech, hard).
wood(beech, soft_red).
wood(beech, largetexture).
wood(yew, leafed).
wood(yew, veryhard).
wood(yew, dark).
...
what_wood(A, B, C, D, E):-
wood(A, B), wood(A, C), wood(A, D), wood(A, E).
what_wood(A) :-
write('Leafes: '), read(X), convert(X, Leaves),
write('Softness: '), read(Y), convert(Y, Softness),
write('Color: '), read(Z), convert(Z, Color),
write('On touch: '), read(Q), convert(Q, OnTouch),
what_wood(A, Leaves, Softness, Color, OnTouch).
convert(any, _) :-
!.
convert(Attrib, Attrib).
This code returns same answers like
A = oak ;
A = oak ;
...
A = beech ;
A = beech .
But this is other story which have nothing to do with current question.
Assuming that the number of wood attributes is fixed, four in your example, you can define a predicate wood/5with facts such as:
% wood(Wood, Leaves, Softness, Color, OnTouch).
wood(oak, leafed, hard, gray_brown, smalltexture).
Then, you can modify your what_wood/1 predicate such that when the user enters the atom any for an attribute, it uses an anonymous variable when trying to match wood/5 facts. Something like:
what_wood(Wood) :-
write('Leafes: '), read(Leafes0), convert(Leafes0, Leafes),
write('Softness: '), read(Softness0), convert(Softness0, Softness),
write('Color: '), read(Color), convert(Color0, Color),
write('On touch: '), read(OnTouch), convert(OnTouch0, OnTouch),
wood(Wood, Leaves, Softness, Color, OnTouch).
convert(any, _) :-
!.
convert(Attribute, Attribute).
The next step would be to check the validity of the values entered by the user and e.g. repeat the question if invalid. For example, you could define a read_attribute/2 predicate that would do the reading repeating it until the user enters a valid value:
read_attribute(Attribute, Value) :-
repeat,
write('Value for '), write(Attribute), write(': '),
read(Value),
valid_attribute(Attribute, Value),
!.
valid_attribute(leafes, leafed).
valid_attribute(leafes, coniferous).
valid_attribute(leafes, any).
...
This can be improved in several ways. E.g. by printing the possible values for an attribute when asking its value so that the user knows what is accepted as valid values. The predicate valid_attribute/2 can also be rewritten to avoid creating choice points when testing. You can also rewrite this predicate to take advantage of the facts you already have for valid attributed values:
valid_attribute(Attribute, Value) :-
Test =.. [Attribute, Value],
once(Test).
Prolog is a language with a clean relational data model. I would choose a different schema, separating each attribute: like
wood(oak, leafes(leafed)).
wood(oak, softness(hard)).
...
in this way you can rely on the usual relational patterns to apply in your 'application'. Specifically, Prolog use queries as procedures...

Resources