Prolog - Arithmetic mean and merge - prolog

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.

Related

Non-destructive universal quantification in Prolog

A good language for logic programming should allow the programmer to use a language close to the language used by the mathematicians. Therefore, I have always considered the lack of proper universal quantifier in Prolog an important shortcoming.
Today an idea came to me how to define something much better than forall and foreach.
forany(Var, {Context}, Condition, Body)
This predicate tries to prove Body for all instantiations Var gets successively on backtracking over Condition. All variables in Condition and Body are considered local unless listed in Var or Context. Condition is not permitted to modify in any way the variables listed in Context, otherwise forany won't work correctly.
Here is the implementation (based on yall):
forany(V, {Vars}, Goal1, Goal2) :-
( bagof(V, {V,Vars}/Goal1, Solutions)
-> maplist({Vars}/[V]>>Goal2, Solutions)
; true ).
My first question is about the second argument of forany. I'd like to eliminate it.
Now some examples
Construct a list of the first 8 squares:
?- length(X,8), forany(N, {X}, between(1,8,N),
(Q is N*N, nth1(N, X, Q))).
X = [1, 4, 9, 16, 25, 36, 49, 64].
Reverse a list:
?- X=[1,2,3,4,5], length(X,N), length(Y,N),
forany(I, {X,Y,N}, between(1,N,I),
(J is N-I+1, nth1(I,X,A), nth1(J,Y,A))).
X = [1, 2, 3, 4, 5],
N = 5,
Y = [5, 4, 3, 2, 1].
Subset:
subset(X, Y) :- forany(A, {X,Y}, member(A,X), member(A, Y)).
A funny way to generate all permutations of a list without duplicates:
permutation(X, Y) :-
length(X, N), length(Y, N), subset(X, Y).
?- permutation([1,2,3],X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
A funny way to sort a list of different integers. Notice that constraints are used to make the list sorted so most permutations won't be generated:
sorted(X) :- forany(A-B, {X}, append(_, [A,B|_], X),
A#<B).
?- X=[7,3,8,2,6,4,9,5,1], length(X, N), length(Y, N),
sorted(Y), subset(X,Y).
X = [7, 3, 8, 2, 6, 4, 9, 5, 1],
N = 9,
Y = [1, 2, 3, 4, 5, 6, 7, 8, 9] .
The problem
It seems that this forany works brilliantly when constraints are not used. Also, it can be used to generate constraints, but at least on SWI-Prolog there are problems when constraints already have been generated. The reason for this is that forany uses bagof and according to the manual of SWI-Prolog:
Term-copying operations (assertz/1, retract/1, findall/3, copy_term/2, etc.) generally also copy constraints. The effect varies from ok, silent copying of huge constraint networks to violations of the internal consistency of constraint networks. As a rule of thumb, copying terms holding attributes must be deprecated. If you need to reason about a term that is involved in constraints, use copy_term/3 to obtain the constraints as Prolog goals, and use these goals for further processing.
Here is a demonstration of the problem bagof creates with constraints:
?- X=[A,B,C], dif(C,D), bagof(_, K^member(K,X), _).
X = [A, B, C],
dif(C, _5306),
dif(C, _5318),
dif(C, _5330),
dif(C, D).
As you can see, three unnecessary constraints are created.
My second question is if this is a problem only of SWI-Prolog.
And the third question: is there a way to fix this in SWI-Prolog. The above quote from the manual suggests that copy_term/3 should be used. Unfortunately, I don't understand this suggestion and I don't know if it is useful for forany.
Great news! I was surprised that bagof is written in Prolog. By looking at its code I learned that some things I thought are impossible are in fact possible. And just as the manual of SWI-Prolog suggested, copy_term/3 or rather the similar predicate copy_term_nat/2 helped.
So with great joy I am able to present a fully working (as far as I can tell) universal quantifier for SWI-Prolog:
forany(V, {Vars}, Condition, Body) :-
findall(V-Vars, Condition, Solutions),
% For SWI-Prolog. Can be replaced by Solutions=Clean_solutions in other systems
copy_term_nat(Solutions, Clean_solutions),
forany_execute_goals(Clean_solutions, Vars, V, Body).
forany_execute_goals([], _, _, _).
forany_execute_goals([Sol-NewVars|Solutions], Vars, V, Body) :-
% The following test can be removed
assertion(subsumes_term(NewVars, Vars)),
% or replaced by the following more standard use of throw/1:
% ( subsumes_term(NewVars, Vars)
% -> true
% ; throw('Forbidden instantiation of context variables by the antecedent of forany') ),
NewVars = Vars,
call({Vars}/[V]>>Body, Sol),
forany_execute_goals(Solutions, Vars, V, Body).

How to specify a range in nondet (to avoid choicepoints in PL Unit Testing)

I wrote some tests for some Prolog codes and got "Test succeeded with choicepoint" warning. Here's one of my tests:
test(overlap_intervals):-
overlap_intervals([0, 10], [5, 15]).
Here's the codes:
%% precondition: X1<Y1 and X2<Y2
overlap_intervals([X1,Y1], [X2,Y2]):-
(X1<X2, Y1<Y2, Y1>X2); !,
(X1>X2, Y1>Y2, Y1<X2).
I saw from other posts that I can use "nondet" test(foo, nondet) :- .... to avoid choice points, such as:
test(member, all(X == [a,b,c])) :-
member(X, [a,b,c]). % quoting Prolog document
However, I'm dealing with intervals in my case. The "nondet" would be a range of numbers, rather than an exhaustible list. For example (which apparently doesn't work but gets my goal across):
test(overlap_intervals, all(Y<10, Y>0)):-
overlap_intervals([0, 10], [Y, 15]).
Can someone explain why there are choicepoints and how to avoid them in my case?
There is a choicepoint because you got confused about precedence when mixing ; and !. As usual in logic, and (,) binds stronger than or (;).
Better ways of writing your predicate would be:
overlap_intervals_2([X1,Y1], [X2,Y2]):-
( (X1<X2, Y1<Y2, Y1>X2),
!
; (X1>X2, Y1>Y2, Y1<X2) ).
or (preferred):
overlap_intervals_3([X1,Y1], [X2,Y2]):-
(X1<X2, Y1<Y2, Y1>X2),
!.
overlap_intervals_3([X1,Y1], [X2,Y2]):-
(X1>X2, Y1>Y2, Y1<X2).
These alternative definitions make it clearer where the cut belongs, highlighting that it was misplaced in your version. Tests:
?- overlap_intervals([0, 10], [5, 15]).
true ;
false.
?- overlap_intervals_2([0, 10], [5, 15]).
true.
?- overlap_intervals_3([0, 10], [5, 15]).
true.
Note that none of these definitions cover the case where one interval is contained within another one. I would say that overlap_intervals([0, 10], [1, 9]) should be true, but the exact meaning of the predicate depends on your application, of course.
As for your use of all in a test head, all(between(0, 10, Y)) should work, I think (not tested).

Creating an Identity Matrix in Prolog

I have to write the predicate identity/2 which receives a number n, and makes an identity matrix [n x n].
Example:
identity(3,I).
I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
For this one I do not even know how to start. At least an insight on how to build a simple list of n elements could provide me a good starting point! Thank you!
Well, the first thing you need to do is worry about your base case, which I'll just give you:
identity(1, [[1]]).
Now you need to make it work inductively for the rest.
Personally, I would write some helper predicates, like this to produce a list of zeroes:
zeroes(0, []).
zeroes(N, [0|Rest]) :- succ(N0, N), zeroes(N0, Rest).
You can also generate lists of arbitrary size using length/2:
?- length(X, 3).
X = [_G1563, _G1566, _G1569].
Other predicates that might be helpful to you: nth1/3:
?- length(X, 3), nth1(1, X, foo), nth1(3, X, bar).
X = [foo, _G1658, bar].
And don't forget you can prepend to a list just by using [X|Rest]. :) Good luck!

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.

prolog: sorting w.r.t. some attribute

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

Resources