prolog: sorting w.r.t. some attribute - prolog

My database is similar to this:
% happy(Person,Happiness)
happy(1,10).
happy(2,5).
happy(3,8).
happy(4,1).
I want to sort people w.r.t. their happiness.
I coded the following and it does what I want. However it looked cumbersome to me. Any improvements?
? - sortPeople(Ts).
Ts = [1, 3, 2, 4].
My solution:
getFirst([],R,R).
getFirst([[H1,_]|T],F,R) :-
append([H1],F,R1),
getFirst(T,R1,R).
compareHappiness(X, [_,S1], [_,S2]) :- compare(X, S1, S2).
sortPeople(Ts) :-
findall([X,Y], happy(X,Y), List),
predsort(compareHappiness, List, SortedList),
getFirst(SortedList,[],Ts).

Consider using more descriptive and declarative predicate names, for example:
person_happiness(1, 10).
person_happiness(2, 5).
person_happiness(3, 8).
person_happiness(4, 1).
To sort people by happiness, consider using the built-in keysort/2, which is more efficient than predsort/3. You only need to build key-value pairs, for which by convention the functor -/2 is used, and instead of your auxiliary predicate, consider using the SWI-Prolog built-ins pairs_values/2 and reverse/2:
descending_happiness(Ps) :-
findall(H-P, person_happiness(P, H), HPs),
keysort(HPs, HPs1),
pairs_values(HPs1, Ps1),
reverse(Ps1, Ps).
Example query:
?- descending_happiness(Ps).
Ps = [1, 3, 2, 4].

-Here is what I got :
sort(Rez) :- findall([Happiness,PId],happy(PId,Happiness),List),
msort(List,LSorted),
findall(PersonID,member([_,PersonID],LSorted),Sorted),
reverse(Sorted,Rez).

Related

Prolog - Arithmetic mean and merge

I have this query in Prolog:
?- order([[person1, [7,8,8,9]], [person2, [8,9,8,9]], [person3,
[6,7,5,4]]],X).
I need the arithmetic mean of each person and then use mergesort, something like this:
X=[[person2,[8,9,8,9],8.5],[person1,[7,8,8,9],8],[person3,[6,7,5,4],5.5]].
I know how to get the arithmetic mean for one list, but in this case I will need something recursive, I think.
Someone can help me?
Applying a predicate to each member of a list to obtain a new list is what maplist is for. For sorting, you can use the built-in keysort/2, or, if you have the latest SWI-Prolog version, sort/4. However, it would be better if you used pairs (for example) and not just another item in a list (you know the number of "things", after all):
person_vals_mean([P, Vs], Mean-[P, Vs]) :-
numlist_mean(Vs, Mean). % assuming you have defined it elsewhere
order(PVs, Ordered) :-
maplist(person_vals_mean, PVs, PVMs),
keysort(PVMs, Ordered_rev),
reverse(Ordered_rev, Ordered).
It might be wise to also get rid of the lists altogether: why [person, [1,2,3]] and not person_vals(person, [1,2,3])? With this representation, you can rewrite person_vals_mean/2 as:
person_vals_mean(person_vals(P, Vs), Mean-person_vals(P, Vs)) :- ...
Or, if you can use sort/4, even:
person_vals_mean(person_vals(P, Vs), person_vals_mean(P, Vs, M)) :-
numlist_mean(Vs, M).
order_by_means(PVs, Ordered) :-
maplist(person_vals_mean, PVs, PVMs),
sort(3, #>=, PVMs, Ordered).
And then:
?- order_by_means([person_vals(person1, [7,8,8,9]),
person_vals(person2, [8,9,8,9]),
person_vals(person3, [6,7,5,4])],
Ordered).
Ordered = [person_vals_mean(person2, [8, 9, 8, 9], 8.5),
person_vals_mean(person1, [7, 8, 8, 9], 8),
person_vals_mean(person3, [6, 7, 5, 4], 5.5)].
As a matter of fact, all sorting built-ins in SWI-Prolog use a merge sort algorithm, but this is implemented in C and really quite irrelevant.

List from nested predicates

I want to know if it's possible to get a list of numbers from nested predicates in prolog.
I'll make an example, from:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X).
I want in X this:
X = [0,5,1,8,7,3,6,5].
Thank you if you can help me =)
Whenever you write predicates involving general term traversal, always keep in mind that such predicates will be limited in the way they can be used. Let's call your relation term_subtermnumbers/2 which relates a term to the list of numbers that occur in it as subterms in the order of their left-to-right appearance, including multiple occurrences. First, you may think of examples you gave, like
?- term_subtermnumbers(p(f(0,5,1), k(8, f(7,3), h(6)), 5), Numbers).
Numbers = [0, 5, 1, 8, 7, 3, 6, 5].
But what if you turn the query around, and ask instead:
?- term_subtermnumbers(Term, [0, 5, 1, 8, 7, 3, 6, 5]).
There are many possibilities for solutions. In fact, infinitely many. Or take a simpler query:
?- term_subtermnumbers(Term, []).
That is, all the Terms that do not contain numbers.
Since the set of solutions is infinite here, and there is no way to abbreviate that set meaningfully as answers, it really makes sense to produce in that case a special error called an instantiation error.
Some - not all - Prolog built-ins ensure this property. (=..)/2 and functor/3 are faithfully guaranteeing that property. Helas, number/1 is not. As a consequence, always use (=..)/2 or functor/3 prior to number/1, atom/1, atomic/1 and some others.
term_subtermnumbers(Term, Numbers) :-
phrase(subtermnumbers(Term), Numbers).
subtermnumbers(Term) -->
{ Term =.. [_| Args] },
( {number(Term)} -> [Term]
; args_subtermnumbers(Args)
).
args_subtermnumbers([]) --> [].
args_subtermnumbers([Arg|Args]) -->
subtermnumbers(Arg),
args_subtermnumbers(Args).
If your Prolog has append/2 and maplist/3:
elements(N, [N]) :- number(N), !.
elements(S, Ss) :- S=..[_|Es], maplist(elements, Es, Ts), append(Ts, Ss).
This worked for me:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(_(|Y),Z) :- elements(Y,Z).
writenl(X) :- write(X), nl.
It gave me [0, 5, 1, 8, 7, 3, 6, 5].
Try this instead:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(X,Z) :- X=..[_|Y], elements(Y,Z).
writenl(X) :- write(X), nl.

How to check which items on the list meet certain condition?

How to make a function called busLineLonger, which receives at least two parameters to decide if a bus line is longer or not?
*/This is how it works*/
* busStops(number_of_the_bus,number_of_stops)*/
/*?- busLineLonger([busStops(1,7),busStops(2,4),busStops(3,6)],5,WHICH).
* WHICH = [1,3].
Using only comparative things, like #> <# /==#.
Sorry my english
Edit...
So far I've think of something like this
busLineLonger([busStops(A,B)|R],N,[_|_]):-
N#>B,
busLineLonger(R,N,A).
Here's how you could do it using meta-predicates,
reified test predicates,
and lambda expressions.
:- use_module(library(lambda)).
First, we define the reified test predicate (>)/3 like this:
>(X,Y,Truth) :- (X > Y -> Truth=true ; Truth=false).
Next, we define three different implementations of busLineLonger/3 (named busLineLonger1/3, busLineLonger2/3, and busLineLonger3/3) in terms of the following meta-predicates: maplist/3, tfilter/3, tfiltermap/4, and tchoose/3. Of course, in the end we will only need one---but that shouldn't keep us from exploring the various options we have!
#1: based on tfilter/3 and maplist/3
Do two separate steps:
1. Select items of concern.
2. Project those items to the data of interest.
busLineLonger1(Ls0,N,IDs) :-
tfilter(\busStops(_,L)^(L>N), Ls0,Ls1),
maplist(\busStops(Id,_)^Id^true, Ls1, IDs).
#2: based on tfiltermap/4
Here, we use exactly the same lambda expressions as before, but we pass
them both to meta-predicate tfiltermap/4. Doing so can help reduce
save some resources.
busLineLonger2(Ls,N,IDs) :-
tfiltermap(\busStops(_,L)^(L>N), \busStops(Id,_)^Id^true, Ls,IDs).
Here's how tfiltermap/4 can be implemented:
:- meta_predicate tfiltermap(2,2,?,?).
tfiltermap(Filter_2,Map_2,Xs,Ys) :-
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys).
:- meta_predicate list_tfilter_map_list(?,2,2,?).
list_tfilter_map_list([],_,_,[]).
list_tfilter_map_list([X|Xs],Filter_2,Map_2,Ys1) :-
if_(call(Filter_2,X), (call(Map_2,X,Y),Ys1=[Y|Ys0]), Ys1=Ys0),
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys0).
#3: based on tchoose/3
Here we do not use two separate lambda expressions, but a combined one.
busLineLonger3(Ls,N,IDs) :-
tchoose(\busStops(Id,L)^Id^(L>N), Ls,IDs).
Here's how tchoose/3 can be implemented:
:- meta_predicate tchoose(3,?,?).
tchoose(P_3,Xs,Ys) :-
list_tchoose_list(Xs,P_3,Ys).
:- meta_predicate list_tchoose_list(?,3,?).
list_tchoose_list([],_,[]).
list_tchoose_list([X|Xs],P_3,Ys1) :-
if_(call(P_3,X,Y), Ys1=[Y|Ys0], Ys1=Ys0),
list_tchoose_list(Xs,P_3,Ys0).
Let's see them in action!
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger1(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger2(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger3(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
Done!
So... what's the bottom line?
Many meta-predicates are versatile and can be used in a lot of sitations similar to the one here.
Implementing these meta-predicates is a one time effort that is amortized quickly.
Many meta-predicates handle the "recursive part", which enables you to focus on actual work.
Often, with meta-predicates (as with regular ones), "there's more than one way to do things".
Depending on the concrete circumstances, using a particular meta-predicate may be better than using another one, and vice versa.
For this question, I think, implementation #3 (the one using tchoose/3) is best.
Some things to fix in your code:
3rd argument is [_|_], that is the result are free variables... doesn't make sense. You need two cases: one in which the B is greater than N and you include the result; the other in which B is less or equal than N, and you don't include that result.
base case is missing. what's the result when bus list is empty?
A possible solution:
busLineLonger([],_,[]).
busLineLonger([busStops(A,B)|R],N,[A|S]) :- B>N, busLineLonger(R,N,S).
busLineLonger([busStops(_,B)|R],N,S) :- B=<N, busLineLonger(R,N,S).
?- busLineLonger([busStops(1,7),busStops(2,4),busStops(3,6)],5,WHICH).
WHICH = [1, 3]

How do I append 3 lists efficiently in Prolog?

I know how to do it for 2 lists:
append([],L,L).
append([H|T],L,[H|R]):-append(T,L,R).
but how to do it for 3? Without using the append for 2 lists twice.
To append lists efficiently, consider using difference lists. A difference list is a list expressed using a term with two lists. The most common representation uses (-)/2 as the functor for the term. For example, the list [1,2,3] can be expressed as:
[1,2,3| Tail]-Tail.
By keeping track of the list tail, i.e. of its open end, you can do several operations efficiently. For example, you can append an element to end of the list in O(1) by instantiating the tail:
add_to_end_of_list(List-Tail, Element, List-Tail2) :-
Tail = [Element| Tail2].
Or simply:
add_to_end_of_list(List-[Element| Tail2], Element, List-Tail2).
Let's try it:
?- add_to_end_of_list([1,2,3| Tail]-Tail, 4, Result).
Tail = [4|_G1006],
Result = [1, 2, 3, 4|_G1006]-_G1006.
Now, appending two lists is similar and also O(1). Instead of appending an element, we want to append a list of elements:
dappend(List1-Tail1, Tail1-Tail2, List1-Tail2).
For example:
?- dappend([1,2,3 | Tail1]-Tail1, [4,5,6| Tail2]-Tail2, Result).
Tail1 = [4, 5, 6|Tail2],
Result = [1, 2, 3, 4, 5, 6|Tail2]-Tail2.
I leave to you as an exercise to answer your own question using difference lists. Note that going from a difference list to a closed list, is simply a question of instantiating the open end to the empty list. For example:
?- dappend([1,2,3 | Tail1]-Tail1, [4,5,6| Tail2]-Tail2, Result-[]).
Tail1 = [4, 5, 6],
Tail2 = [],
Result = [1, 2, 3, 4, 5, 6].
However, going from a closed list to a difference list does requires you to traverse the list, which is O(n):
as_difflist([], Back-Back).
as_difflist([Head| Tail], [Head| Tail2]-Back) :-
as_difflist(Tail, Tail2-Back).
The cost of constructing the difference lists may or may not be an issue, of course, depending on how you get the initial lists and how often you will be appending lists in your application.
Hope I understood the question (and I don't think the following is more efficient than the other solutions here), but did you mean something like this?
append([],[],L,L).
append([],[H|T],L,[H|R]) :- append([],T,L,R).
append([H|T],L0,L1,[H|R]) :- append(T,L0,L1,R).
append3(Xs, Ys, Zs, XsYsZs) :-
append(Xs, YsZs, XsYsZs),
append(Ys, Zs, YsZs).
Is as efficient, as it can get. Cost is about |Xs|+|Ys| inferences. However, you might have attempted to define it like the following with about 2|Xs|+|Ys| inferences.
append3bad(Xs, Ys, Zs, XsYsZs) :-
append(Xs, Ys, XsYs),
append(XsYs, Zs, XsYsZs).
Also, termination is much better in the first case:
append3(Xs, Ys, Zs, XsYsZs)
terminates_if b(Xs),b(Ys);b(XsYsZs)
meaning that either Xs and Ys or XsYsZs needs to be known to make append3/4 terminate
... versus
append3bad(Xs, Ys, Zs, XsYsZs)
terminates_if b(Xs),b(Ys);b(Xs),b(XsYsZs)
^^^^^
for append3bad/4, where XsYsZs is not sufficient, but additionally also Xs has to be known.

How to get a list of possible predicate values

Lets I have a predicate p/1 defined, for example, as follows:
p(2).
p(3).
p(5).
p(7).
How can I define a predicate p_list/1 which will be true for a list of all possible values of p/1 (in above case - [2, 3, 5, 7]) in the backtracking order?
Simple enumeration of the values is not acceptable because it makes maintenance more difficult. Moreover values, can be defined implicitly.
You can use bagof(X, p(X), L) which gives you L = [2,3,5,7].
What do you mean by "defined implicitly" ? Can you give an example.
maplist/2 works well for check, while findall/3 it's the basic list constructor in Prolog
Try
?- findall(X, p(X), L), maplist(p, L).

Resources