Nikita Kalugin
Nikita Kalugin

Reputation: 742

Sort a list in 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]]

Upvotes: 0

Views: 245

Answers (1)

lurker
lurker

Reputation: 58244

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

Upvotes: 2

Related Questions