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

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

Related

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.

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...

How to findall for universal facts in prolog?

In Prolog I can write
child(martha,charlotte).
child(charlotte,caroline).
child(caroline,laura).
child(laura,rose).
descend(X,Y) :-
child(X,Y).
descend(X,Y) :-
child(X,Z),
descend(Z,Y).
And then write
?- findall(X,descend(martha,X),Z).
and get four solutions
Z = [charlotte,caroline,laura,rose]
But if I then add an universal fact
likes(X,pomegranate).
and try
?- findall(X,likes(X, pomegranate),Z).
I get:
Z = [_G17].
What is that _G17?
What do I need to change to get essentially all variables? ( since likes(X,pomegranate) should mean everything likes pomegranate... right?) :
Z = [martha,charlotte,caroline,laura,rose]
Two solutions. The clean solution is to have a table that lists all "things" in the universe you describe:
person(martha).
person(charlotte).
% etc
And then your "likes" would be rather:
person_likes(P, pomegranate) :-
person(P).
You can also try to hack around it:
person(P) :- child(P, _).
person(P) :- child(_, P).
But this is... unsatisfactory? Think about a relational database: you would have two tables:
CREATE TABLE person (
id INTEGER PRIMARY KEY, -- usually autogenerated
name TEXT NOT NULL
);
CREATE TABLE parent_child (
parent_id INTEGER NOT NULL,
child_id INTEGER NOT NULL,
FOREIGN KEY parent_id REFERENCES person(id),
FOREIGN KEY child_id REFERENCES person(id)
);
The only reason, as far as I am aware, why you don't do the exact same thing in Prolog, is that most introductory tutorials are trying to seduce you and avoid going into such details. And of course the Prolog "database" is not a true relational database (for example, argument position does matter!).
TL;DR You can't avoid thinking about Prolog's resolution strategy when using Prolog.

A prolog assignment which uses lists

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

Data structure, best practice to build true "related by tags" items list?

What I mean saying true "related by tags" list?
Let's imagine that article have 3 tags: A, B, C. True "related by tags" articles for this item will be articles fistly having A, B, C tags, then (A, B), (A, C), (B, C) etc.
table: tags
tag_id
tag_title
tag_nicetitle
table: tags2articles
article_id
tag_id
Using this tables structure is too difficult to calculate true "related by tags".
We can add one more table containing article_id and it's md5(A,B,C). Before hashing we should sort tags by alphabet.
table: article_tags_hashed
id
article_id
md5
count
This table will help us to find articles containing exact set of tags (A,B,C), but it won't help to find articles containing only (A, B), (A, C), (B, C)
What is the best practice?
PS: Sorry for my english, it's pretty bad.
I don't think you need article_tags_hashed because you can group and count the results when you
query the tags2articles.
Example:
select article_id, count(article_id) as tagcnt from tags2articles
where tag_id in (...)
group by article_id
order by tagcnt desc
The articles which contain the most tags will be placed first.

Resources