A prolog assignment which uses lists - prolog

I have an assignment that's driving me crazy.... Here's the assignment:
The following contacts for people in a company are kept as facts as the followings:
phones(joao, [home/217777777, mobile91/917777777]).
phones(maria, [work/218888888, mobile93/938888888, mobile91/918888888]).
phones(jose, [home/213333333, mobile91/914444444]).
Define predicates in Prolog that allow to answer the following question:
1) Which are the possible contacts availiable if you want to phone to any person in a group?
For example:
?- all_contacts([joao,jose],ListOfContacts).
ListOfContactss = [home/217777777, mobile91/917777777, home/213333333,mobile91/914444444]
What I've done is this:
all_contacts([],_):-[].
all_contactos([X|T],LIST):-phones(X,LIST),all_contacts(T,Y).
However if I do:
?- all_contactos([jose,maria],LIST_CONTACTS).
LIST_CONTACTS = [casa/213333333, movel91/914444444].
That is, I only get the contacts for the first person on the list.
Thanks for any help.

You can use the meta predicate findall to collect the phones for all contacts of an input list:
all_contacts(People, Contacts):-
findall(Contact,
( member(Person, People), % For each person
phones(Person, LContact), % get the list of contacts of person
member(Contact, LContact) % collect each contact from that list
), Contacts). % Finally gather all contacts in one list
For your example this yields:
all_contacts([jose,maria],LIST_CONTACTS).
LIST_CONTACTS = [home/213333333, mobile91/914444444, work/218888888, mobile93/938888888, mobile91/918888888].
If you don't want to use builtin predicate you can achieve the same thing using recursive predicates by iterating over all the people and then over every phone for each person, and building a list of contacts along the recursion:
all_contacts(People, Contacts):-
all_contacts(People, [], Contacts).
all_contacts([], Contacts, Contacts).
all_contacts([Person|People], Contacts, NContacts):-
phones(Person, LContacts),
all_contacts_person(LContacts, Contacts, MContacts),
all_contacts(People, MContacts, NContacts).
all_contacts_person([], Contacts, Contacts).
all_contacts_person([Contact|LContacts], Contacts, MContacts):-
all_contacts_person(LContacts, [Contact|Contacts], MContacts).

Related

How to optimize and improve neo4j cypher query with multiple match and an increase number of where clauses

in my neo4j graph database I have some Restaurants linked to Foods listed on the menu and also linked to the Cities where they are located.
I’m trying to search for restaurants in London each offering different kind of foods one from the other, so I started looking for two different restaurants in this way:
MATCH (f1:Food)--(r1:Restaurant)-[:LOCATED_IN]-(c1:City{name:'London'})
WHERE f1.name in [name1, name2, name3, name4]
MATCH (f2:Food)--(r2:Restaurant)-[:LOCATED_IN]-(c2:City{name:'London'})
WHERE r1.id<>r2.id and f1.name<>f2.name
RETURN, r1.name as Restaurant_A, collect(distinct f1.name) as Food_A,r2.name as Restaurant_B, collect(distinct f2.name) as Food_B
LIMIT 1
but if I want to find a third Restaurant I need to add more where clauses and so on if I want a fourth restaurant, a fifth one…
This is the example for the third restaurant:
MATCH (f1:Food)--(r1:Restaurant)-[:LOCATED_IN]-(c1:City{name:'London'})
WHERE f1.name in [name1, name2, name3, name4]
MATCH (f2:Food)--(r2:Restaurant)-[:LOCATED_IN]-(c2:City{name:'London'})
WHERE r1.id<>r2.id and f1.name<>f2.name
MATCH (f3:Food)--(r3:Restaurant)-[:LOCATED_IN]-(c3:City{name:'London'})
WHERE r2.id<>r1.id and r3.id<>r2.id and f3.name<>f3.name and f3.name<>f2.name
RETURN r1.name as Restaurant_A, collect(distinct f1.name) as Food_A,r2.name as Restaurant_B, collect(distinct f2.name) as Food_B, r3.name as Restaurant_C, collect(distinct f3.name) as Food_C
LIMIT 1
And I’d really like to know if there is an alternative way to do it, I’m new to neo4j and every suggestion is more than welcome.
Assuming that you provide a collection of :Food nodes in an array, you could do
MATCH (f:Food)--(r:Restaurant)-[:LOCATED_IN]-(c1:City{name:'London'})
WHERE f IN $foodNodes
RETURN r.name as restaurant, COLLECT(DISTINCT f.name) AS foods
to retrieve the restaurants and their foods.

Retrieve data from database without printing

How do I retrieve data from the database to use it in a condition, but I don't want to print it the console.
Problem I am doing is to retrieve a child from a database whose parents age differ by 15 years.
This is the code I am using which works and prints the year of both parents.
family(person(_,_,date(_,_,Year1),_),
person(_,_,date(_,_,Year2),_),
[person(Name,Surname,_,_)|Y]), abs(Year1-Year2) >= 15.
Define a predicate rule (in a source file) using the query as its body. For example:
child_with_parents_age_gap(Gap, Name, Surname) :-
family(
person(_,_,date(_,_,Year1),_),
person(_,_,date(_,_,Year2),_),
[person(Name,Surname,_,_)| _]
),
abs(Year1-Year2) >= Gap.

Prolog book database (how to get results grouped by author?)

I have a the following database of facts like:
...
book(title1, author1),
book(title2, author2),
book(title3, author3),
book(title4, author1),
book(title5, author2),
...
I need to write a prolog query or rule to get titles grouped by the author (author1, author2...). I can't change order of facts in database.
For now I have only query looking like this:
book(T1, A), book(T2, A), T1 \= T2.
but it returns same thing few times
thanks in advance

get the descending order of cost by writing the name of senders in query

I need To give a list of senders by making Prolog rule to show if they are in descending order of their network charge for sending a message. If they are, return true, otherwise return false.
So,I have created below rule which showing me the number in descending order but lets suppose the numbers are the cost of the messages but I don't know how can I put list of senders according to their descending order of cost .
lets suppose 8,4,3,2 is the cost and by the below rule it showing me the correct descending order if i write query like this :-?message([8,4,3,2]).
message(8).
message(4).
message(3).
message(2).
The Rule i created for this data is
message([_]):-!.
message([_]):-!.
message([A,B|T]) :-A >= B,!,message([B|T]).
My original database look like this . Now my query will be like :-?message([sonny,robert,fred,nayna]).
This should return true as they are descending order in their cost.
%message(Sender, Receiver, Date, Cost_of_sending_a_message)
message(sonny,robert,'2012-05-12',8).
message(robert,sarah,'2012-05-12',5).
message(julie,mary,'2012-05-12',6).
message(fred,nayna,'2012-05-13',6).
message(fred,daniel,'2012-05-14',6).
message(nayna,lucia,'2012-05-15',3).
Could you please tell me where I am doing wrong because I want to get the descending order of cost by writing the name of senders in query ?
order_by/2 from SWI-Prolog library(solution_sequences) can help you:
example usage:
?- order_by([desc(C)], message(F,S,D,C)).
C = 8,
F = sonny,
S = robert,
D = '2012-05-12' ;
C = 6,
F = julie,
S = mary,
D = '2012-05-12'
...
edit:
?- findall(C, (order_by([desc(C)], message(F,S,D,C))), L).
Also library(aggregate), or the classical setof/3 offer possible solutions, but writing it by hand would be the better way to learn Prolog. It depends on what you're after...

List within lists within lists

I have a list called Countries, and each country has a list of Towns, which in it's turn has a list of Streets. And a street has a number of houses. Lists within lists within lists. Very simple.
I need to generate a list of houses that are located in countries which names start with the letter 'A'. Not a very logical example, but it's easier to explain than the more complex structure I'm dealing with.
This is, of course, not too complex and could be done by creating a List and then ForEaching all countries.Where(Name.StartsWith('A')), then ForEaching all towns and finally adding each street in that town to the list.
I don't like that method so I want something prettier...
Could this be done by using something like Aggregate on the Countries.Where() list? If so, how? (Thus, in a single statement.)
Yes, the selection will be on the top list only, so that should make it easier.
This looks like a job for Enumerable.SelectMany (which allows you to ungroup one level of hierarchy):
List<County> countyList = GetCounties();
IEnumerable<County> aCounties = countyList
.Where(c => c.Name.StartsWith("A"));
List<House> aCountyHouses = aCounties
.SelectMany(c => c.Towns)
.SelectMany(t => t.Streets)
.SelectMany(s => s.Houses)
.ToList();

Resources