Sort a list in Prolog - prolog

The fact base stores exam schedule information in the form: exam ('group', 'subject', 'teacher', date). Write a program that allows you to select exam information for the group / teacher, sorting it by date.
So, I already know how to get data from base depending on what you want (by Group number or by teacher name):
exam('426-1', 'PROGRAMMING', 'John', 08-12-2019).
exam('426-1', 'MATH', 'John', 18-12-2019).
exam('426-3', 'ENG', 'Gabe', 16-12-2019).
exam('426-4', 'LITERATURE', 'Homer', 11-12-2019).
findByGroup(Number, Exams) :-
findall([A,B|C], exam(Number, A,B,C), Exams).
findByTeacher(Name, Exams) :-
findall([A,B|C], exam(A, B, Name,C), Exams).
Here's the input
findByGroup('426-1', X).
And here's the output
X = [['PROGRAMMING', 'John'|8-12-2019], ['MATH', 'John'|18-12-2019]]
But I have no idea how should I sort my output by date. For example, how can I sort the data by ascending of the date?
Example:
Before X = [['MATH', 'John'|18-12-2019], ['PROGRAMMING', 'John'|8-12-2019]]
After X = [['PROGRAMMING', 'John'|8-12-2019], ['MATH', 'John'|18-12-2019]]

There are a couple of things that need to be considered:
The date format needs to be in a format that sorts chronologically
The information for each exam item needs to be organized into a term that will sort based upon the date
For the date format, you are using a - functor with integer arguments. This is Ok. To make it sortable chronologically, the order needs to be year-month-day. You can either change your original data to be in that form, or translate to the sortable form before you do your sorting.
Regarding the exam data record, if you create a record formatted as a term with the date as the first argument, then sorting the records will sort by date. For example, any of the following terms will sort by date in Prolog:
exam(Date, Number, A, B)
[Date, Number, A, B]
...etc...
Now we apply this to your problem. Sorting can be done using setof/3 instead of findall/3 if your date is formatted in a sortable form:
% exam(Number, Class, Name, Date)
exam('426-1', 'PROGRAMMING', 'John', 2019-12-08).
exam('426-1', 'MATH', 'John', 2019-12-18).
exam('426-3', 'ENG', 'Gabe', 2019-12-16).
exam('426-4', 'LITERATURE', 'Homer', 2019-12-11).
findByGroup(Number, Exams) :-
setof([Date, Number, Class, Name], exam(Number, Class, Name, Date), Exams).
findByTeacher(Name, Exams) :-
setof([Date, Number, Class, Name], exam(Number, Class, Name, Date), Exams).
This will provide terms with attributes in a slightly different order: [Date, Number, Class, Name]. You can either just work with that, or you could easily change it:
reformat_exam_list([Y-M-D, Number, Class, Name], [Number, Class, Name, D-M-Y]).
And then call maplist(reformat_exam_list, Exams, ReformattedExams).
If you can't change the date format in your original facts, you can pre-process them as part of your setof/3 call.
reformat_sorted_exam(exam(Y-M-D, Number, Class, Name), exam(Number, Class, Name, D-M-Y)).
findByGroup(Number, Exams) :-
setof(exam(Y-M-D, Number, Class, Name), exam(Number, Class, Name, D-M-Y), ExamList),
maplist(reformat_sorted_exam, SortedExams, Exams).
Here I also used an exam structure instead of a list to represent an exam. You get the idea...

Related

Jpa Specification Criteria Api for searching range of dates

If I want to check to apply Predicate for one particular date, I am able to do so as follows:
predicate = builder.between(root.<LocalDate>get('date'),startDate,endDate);
But now I want to apply for a range of dates.
i.e. if I have an appointment from -> 2020-07-20 to -> 2020-07-25 and,
therefore my range of date is (from, to) here I want to put a check in the predicate that, It want to extract appointment for the date 2020-07-23 then It should be between start and end date.
I don't know if this is a possible case. But as mentioned in above example
I want a query like:
predicate = builder.between(root.<LocalDate>get('range(from, to)'),startDate,endDate);
You'll have to break it in to separate predicates based on startDate, endDate, from and to.
I'm not completely sure I got your intention right, but I think you want to check if the interval [from,to] is completely inside the interval [startDate, endDate]
You can do that with code similar to the following:
Predicate startsAfterBegin = builder.greaterThan(root.get('from'), startDate);
Predicate endsBeforeEnd = builder.lessThan(root.get('to'), endDate);
Predicate isContained = builder.and(startsAfterBegin, endsBeforeEnd);

Is there a way to reach the same result in a clause using less variables?

I am trying to find/understand another way to reach a result by calling more predicates in a clause when using less free variables.
Coming from this problematic How to correctly filter a clause that returns multiple duplicated values? I wanted to try reaching to the same result adding a little difficulty.
Facts:
home(peter, sanFrancisco, 1000).
home(ash, sanFrancisco, 100).
home(juan, sanFrancisco, 400).
home(juan, california, 700).
home(ash, california, 600).
home(peter, california, 500).
home(peter, vegas, 100).
home(ash, vegas, 80).
home(juan, vegas, 60).
townHomesTotalCost(sanFrancisco, 1500).
townHomesTotalCost(california, 1800).
townHomesTotalCost(vegas, 240).
Answer provided in the previous question was very helpful and I wanted to try something different to understand better logic programming. Lets say that instead of the home price, I want to compare by percentage. For the percentage I created this clause:
townHomeCostByPercentage(Name, Town, Percentage):-
home(Name, Town, Price),
townHomesTotalCost(Town, Total),
Percentage is round(((Price * 100) / Total)).
Getting the most expensive home in a town is such that no other home in that town is more expensive than it (Lets say I don't want to use the Price as an entry variable):
most_expensive(Name, Town):-
home( Name, Town, Price),
not((home( _, Town, P), P > Price)).
The second most expensive home in a town is such that is the most expensive among the homes which are not the most expensive home in that town:
second_most_expensive(Name, Town):-
most_expensive(Name, Town),
home(Name, Town, Price),
home(_, Town, Price2), Price < TopPrice,
not((home(_, Town, P), P < TopPrice, P > Price)).
I can't get why this is getting the same result as most_expensive(Name, Town)
And finally, using the percentage, return the Name but comparing percentage in that the most_expensive is < than the second_most_expensive + 20:
top_house_owner(Name) :-
most_expensive(Name, T),
townHomeCostByPercentage(Name, T, TopPercentage),
second_most_expensive(_, T),
townHomeCostByPercentage(_, T, Percentage),
TopPercentage < Percentage + 20.
This also returning the results name of most_expensive(Name, Town) and can't get why.

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.

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.

How do I use the Hive "test in(val1, val2)" built in function?

The Programming Hive book lists a test in built in function in Hive, but it is not obvious how to use it and I've been unable to find examples
Here is the information from Programming Hive:
Return type Signature Description
----------- --------- -----------
BOOLEAN test in(val1, val2, …) Return true if testequals one of the values in the list.
I want to know if it can be used to say whether a value is in a Hive array.
For example if I do the query:
hive > select id, mcn from patients limit 2;
id mcn
68900015 ["7382771"]
68900016 ["8847332","60015163","63605102","63251683"]
I'd like to be able to test whether one of those numbers, say "60015163" is in the mcn list for a given patient.
Not sure how to do it.
I've tried a number of variations, all of which fail to parse. Here are two examples that don't work:
select id, test in (mcn, "60015163") from patients where id = '68900016';
select id, mcn from patients where id = '68900016' and test mcn in('60015163');
The function is not test in bu instead in. In the table 6-5 test is a colum name.
So in order to know whether a value is in a Hive array, you need first to use explode on your array.
Instead of explode the array column, you can create an UDF, as it is explain here http://souravgulati.webs.com/apps/forums/topics/show/8863080-hive-accessing-hive-array-custom-udf-

Resources