Find powers of 2 in a list Prolog - list

I'm trying to create a list in Prolog (SWI Prolog) and check which numbers are powers of 2 and second find how many times a specific number is in the list (in this example I'm trying to find how many times the number 3 is in the list).
For a example, if you ask
?- check([0,2,3,-5,-2,1,8,7,4], MULT2, THREE).
you should see
MULT2=[2,8,4]
THREE=1
My first try to find a solution is to search the list with head and doing head mod 2 = 0 to find all numbers which are powers of 2, but something went wrong and I only get "false" as an answer.

Here's how you can find the "powers of two" in logically-pure way!
Using sicstus-prolog 4.3.5, library(reif) and library(clpz):
:- use_module([library(reif), library(clpz)]).
power_of_two_t(I, T) :-
L #= min(I,1),
M #= I /\ (I-1),
call((L = 1, M = 0), T). % using (=)/3 and (',')/3 of library(reif)
Sample query1 using meta-predicate tfilter/3 in combination with power_of_two_t/2:
?- tfilter(power_of_two_t, [0,2,3,-5,-2,1,8,7,4], Ps).
Ps = [2,1,8,4]. % succeeds deterministically
Here's a more general query suggested by a comment:
?- tfilter(power_of_two_t, [X], Ps).
Ps = [X], 0#=X/\_A, _A+1#=X, X in 1..sup, _A in 0..sup
; Ps = [], dif(_A,0), _A#=X/\_B, _B+1#=X, X in 1..sup, _B in 0..sup
; Ps = [], dif(_A,1), _A#=min(X,1), _B#=X/\_C, _C+1#=X, X#>=_A, _A in inf..1.
Footnote 1: The answer sequences shown above were brushed up to indicate the determinism of calls.
Footnote 2: To reproduce the results use call_det/2 which is defined like this:
call_det(G_0, Det) :-
call_cleanup(G_0, Flag = set),
( nonvar(Flag)
-> Det = true
; Det = false
).

It's a strange thing to have two such a different tasks to do in one predicate. You should probably have two separate predicates, one for counting number of powers of 2 and one to count 3s. Then you can combine them in one predicate like:
check(Nums, MULT2, THREE) :-
count2powers(Nums, MULT2),
count3s(Nums, THREE).
After that you can decompose further and have a separate predicate to check if a number is a power of 2:
is2power(1).
is2power(N) :-
N > 0,
N2 is N // 2,
N2 * 2 =:= N,
is2power(N2).
This is basic software engineering and this way you can build your program step by step and you will be able to ask more concrete and meaningful questions than just "The whole program returns false."

Related

Finding a pattern in a list, prolog

I´m trying to build a predicate pattern(List,Pattern) that takes a List formed only by a repeated pattern and the output has to be that pattern.
Some examples of the List:
List1=[a,b,a,b]
List2=[1,2,3,1,2,3]
List3=[a,a,a,a,a,a,a,a]
As you can see, in each case either the list and the pattern can have different lenghts.
And the output in each case would be:
Pattern1=[a,b]
Pattern2=[1,2,3]
Pattern3=[a]
The only way I can think about a solution is taking the first element of the List (for example, in List2 would be "1") and going through List2 until I find again a "1" and then put in Pattern everything before the second 1 ("123"), but I don´t think it is the best solution. Does anybody know an easier way to solve it? Maybe with Append/3 or Member/2? Thank you!
You are looking for the shortest sequence Q ("pattern") such that list L is n > 0 concatenations of Q (whereby if n = 1 iff Q = L), then
If you have a verifying predicate which verifies that L is indeed a concatenation of a (non necessarily) shortest Q:
multiple_concatenations(X,X). % L = 1 * Q
multiple_concatenations(Q,L) :- % L = N * Q (for some N >= 1, Q <> []) if
concatenation(Q,Rest,L), % L = Q + Rest and
multiple_concatenations(Q,Rest). % Rest = M * Q (for some M)
Where concatenation/3 is just the sanely named append/3 from Prolog:
concatenation(List1,List2,List12) :-
append(List1,List2,List12).
... then you can try to find a shortest Q by just generating longer and longer potential _Q_s (of length going from 1 to length(L)) and break off at the first Q which passes multiple_concatenations(Q,L,N):
shortest_q(Q,L) :-
length(L,Length), % The length of L
must_be(positive_integer,Length), % Enforce length(L) > 0; throws if not
length(Q,N), % Generate a longer and longer
% "list template" Q (i.e. a list with only
% uninstantiated variables) on backtracking,
% with N = 0,1,2,3,...
N>0, % Don't want an empty template though
(concatenation(Q,_,L) % Q's uninstantiated members are
% now instantiated; if this fails,
->
(multiple_concatenations(Q,L), % Check whether Q is acceptable
!) % If yes, cut (i.e. break off at first answer)
;
fail). % If concatenation(Q,_,L) fails, fail the
% predicate: we have
% gone too far (Q is longer than L)
Add a few plunit test cases, which are doubleplus important in the "what am I computing right now?" Prolog wonderland:
:- begin_tests(mq).
test(1) :-
shortest_q(Q,[a,b,a,b]),
assertion(Q == [a,b]).
test(2) :-
shortest_q(Q,[1,2,3,1,2,3]),
assertion(Q == [1,2,3]).
test(3) :-
shortest_q(Q,[a,a,a,a,a,a,a,a]),
assertion(Q == [a]).
test(4) :-
shortest_q(Q,[a,b,c,d,e,f,g]),
assertion(Q == [a,b,c,d,e,f,g]).
:- end_tests(mq).
And so:
?- run_tests.
% PL-Unit: mq .... done
% All 4 tests passed
true.
Note however that "verification mode" accepts a sequence longer than the minimum:
?- shortest_q([1,2,3],[1,2,3,1,2,3]).
true.
?- shortest_q([1,2,3,1,2,3],[1,2,3,1,2,3]).
true.
A simple solution using only append/3 is:
% pattern(+List, -Pattern)
pattern([], _). % Any pattern repeated 0 times gives []
pattern(L, [P|Ps]) :- % [P|Ps] guarantees a non-empty pattern
append([P|Ps], R, L), % Gets a prefix of L as a possible pattern
pattern(R, [P|Ps]), % Checks whether prefix is indeed a pattern
!. % stops when the shortest pattern is found
Examples:
?- pattern([a,b,a,b], P).
P = [a, b].
?- pattern([1,2,3,1,2,3], P).
P = [1, 2, 3].
?- pattern([a,a,a,a,a,a,a], P).
P = [a].

Prolog: compare list elements and sum

New to prolog and trying to implement the following function that takes 3 lists:
True if lists are the same length
True if elements of third list is sum of the two lists
Example: fn([1,2,3],[4,5,6],[5,7,9]) returns true. Note that the sum is element-wise addition.
This is what I have so far:
fn([],[],[]).
fn([_|T1], [_|T2], [_|T3]) :-
fn(T1,T2,T3), % check they are same length
fn(T1,T2,N1), % check that T3=T1+T2
N1 is T1+T2,
N1 = T3.
From what I understand, the error is due to the base case (it has empty lists which causes error with evaluation of addition?)
Thanks for any help and explanations!
In addition to #GuyCoder's answer, I would point out that it is worthwhile to consider using one of the maplist predicates from library(apply) when modifying all elements of lists. You can use a predicate to describe the relation between three numbers...
:- use_module(library(apply)). % for maplist/4
num_num_sum(X,Y,S) :-
S is X+Y.
... and subsequently use maplist/4 to apply it to entire lists:
fn(X,Y,Z) :-
maplist(num_num_sum,X,Y,Z).
This predicate yields the desired results if the first two lists are fully instantiated:
?- fn([1,2,3],[4,5,6],X).
X = [5,7,9]
However, due to the use of is/2 you get instantiation errors if the first two lists contain variables:
?- fn([1,A,3],[4,5,6],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is _+B: expected bound value
?- fn([1,2,3],[4,5,A],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is A+B: expected bound value
If you only want to use the predicate for lists of integers, you can use CLP(FD) to make it more versatile:
:- use_module(library(apply)).
:- use_module(library(clpfd)). % <- use CLP(FD)
int_int_sum(X,Y,S) :-
S #= X+Y. % use CLP(FD) constraint #=/2 instead of is/2
fnCLP(X,Y,Z) :-
maplist(int_int_sum,X,Y,Z).
With this definition the previously problematic queries work as well:
?- fnCLP([1,A,3],[4,5,6],[5,7,9]).
A = 2
?- fnCLP([1,2,3],[4,5,A],[5,7,9]).
A = 6
Even the most general query yields results with this version:
?- fnCLP(X,Y,Z).
X = Y = Z = [] ? ;
X = [_A],
Y = [_B],
Z = [_C],
_A+_B#=_C ? ;
X = [_A,_B],
Y = [_C,_D],
Z = [_E,_F],
_A+_C#=_E,
_B+_D#=_F ? ;
.
.
.
Since the numbers in the above answers are not uniquely determined, you get residual goals instead of actual numbers. In order to get actual numbers in the answers, you have to restrict the range of two of the lists and label them subsequently (see documentation for details), e.g. to generate lists containing the numbers 3,4,5 in the first list and 6,7,8 in the second list, you can query:
label the lists
restrict the domain | |
v v v v
?- fnCLP(X,Y,Z), X ins 3..5, Y ins 6..8, label(X), label(Y).
X = Y = Z = [] ? ;
X = [3],
Y = [6],
Z = [9] ? ;
X = [3],
Y = [7],
Z = [10] ? ;
.
.
.
X = [3,4],
Y = [6,7],
Z = [9,11] ? ;
X = [3,4],
Y = [6,8],
Z = [9,12] ? ;
.
.
.
On an additional note: there are also clp libraries for booleans (CLP(B)), rationals and reals (CLP(Q,R)) that you might find interesting.
From what I understand, the error is due to the base case.
I don't see it that way.
The first problem I see is that you are trying to process list which leads to thinking about using DCGs, but since you are new I will avoid that route.
When processing list you typically process the head of the list then pass the tail back to the predicate using recursion.
e.g. for length of list you would have
ln([],N,N).
ln([_|T],N0,N) :-
N1 is N0+1,
ln(T,N1,N).
ln(L,N) :-
ln(L,0,N).
The predicate ln/2 is used to set up the initial count of 0 and the predicate ln/3 does the work using recursion. Notice how the head of the list is taken off the front of the list and the tail of the list is passed recursively onto the predicate again. When the list is empty the predicate ln([],N,N). unifies, in this case think copies, the intermediate count from the second position into the third position, which it what is passed back with ln/2.
Now back to your problem.
The base case is fine
fn([],[],[]).
There are three list and for each one look at the list as [H|T]
fn([H1|T1],[H2|T2],[H3|T3])
and the call to do the recursion on the tail is
fn(T1,T2,T3)
all that is left is to process the heads which is
H3 is H1 + H2
putting it all together gives us
fn([],[],[]).
fn([H1|T1], [H2|T2], [H3|T3]) :-
H3 is H1 + H2,
fn(T1,T2,T3).
and a quick few checks.
?- fn([],[],[]).
true.
?- fn([1],[1],[2]).
true.
?- fn([1,2],[3,4],[4,6]).
true.
?- fn([1,2],[3,4,5],[4,6,5]).
false.
With regards to the two conditions. When I look at exercises problems for logic programming they sometimes give a condition like True if lists are the same length or some other condition that returns true. I tend to ignore those at first and concentrate on getting the other part done first, in this case elements of third list is sum of the two lists then I check to see if the other conditions are correct. For most simple classroom exercises they are. I sometimes think teacher try to give out these extra conditions to confuse the student, but in reality the are there just to clarify how the code should work.

List - counting atoms related to their previous term

I want to count the number of elements in a list which have a relation with the element following.
The predicate I have works by using an accumulator variable which it increments if the predicate related returns true.
The following example code is to check the number of times an element is greater than it's previous element.
So for example
count_list([1,2,3,2,1,3,2],Count).
should return 3.
The code almost works. It increments the accumulator variable correctly. However, the function returns false, when it tries to compare the final 2 at the end with the non-existent next term.
listofitems([],N,N).
%count number of items which are related to the previous
listofitems([A,B|T],Acc,N) :-
write(A),write(' '), write(B),
( related(A,B) -> Acc1 is Acc+1 ; Acc1 = Acc ),
write(Acc1),write('\n'),
listofitems([B|T],Acc1,N).
count_list(L,N):-
listofitems(L,0,N).
%define the relationship to be counted
related(A,B):-
B>A.
Does anyone have any suggestions as to how to create an elegant terminating condition so I can return the accumulated value?
Does anyone have any suggestions as to how to create an elegant terminating condition so I can return the accumulated value?
The problem you have is that your query fails. Try first to minimize the query as much as possible. Certainly, you expect it to work for:
?- listofitems([], Count).
Count = 0.
Yet, it already fails for:
?- listofitems([1], Count).
false.
So let's try to dig into the reason for that.
And since your program is pure (apart from those writes), it is possible to diagnose this a little better by considering a generalization of your program. I prefer to look at such generalizations as I do not want to read too much (eye strain and such):
:- op(950, fy, *).
*_.
listofitems([], N,N).
listofitems([A,B|T], Acc,N) :-
* ( related(A,B) -> Acc1 is Acc+1 ; Acc1 = Acc ),
* listofitems([B|T], Acc1,N).
count_list(L,N):-
listofitems(L,0,N).
?- count_list([1], Count).
false.
Even this generalization fails! So now in desperation I try to ask the most general query. It's like when I ask one thing after the other and get a noe after a no. Good this is Prolog, for we can ask: "Say me just everything you know".
?- count_list(Es,Count).
Es = [], Count = 0
; Es = [_,_|_].
So it is only the case for the empty list and lists with at least two elements. But there is no answer for one-elemented lists! You will thus have to generalize the program somehow.
A natural way would be to add a fact
listofitems([_], N, N).
As a minor remark, this isn't called a "terminating condition" but rather a "base case".
And if you really want to trace your code, I recommend these techniques instead of adding manual writes. They are much too prone to error.
If the all list items are integers and your Prolog system supports clpfd, you can proceed like this:
:- use_module(library(clpfd)).
:- use_module(library(lists), [last/3]).
:- use_module(library(maplist), [maplist/4]).
To relate adjacent items, look at two sublists of [E|Es], Es and Fs. If, say,
[E|Es] = [1,2,3,2,1,3,2] holds ...
... then Fs lacks the last item (Fs = [1,2,3,2,1,3,2]) ...
... and Es lacks the first item (Es = [1,2,3,2,1,3,2]).
maplist/4 and i0_i1_gt01/3 map corresponding list items in Fs and Es to 0 / 1:
i_j_gt01(I, J, B) :- % if I #< J then B #= 1
I #< J #<=> B. % if I #>= J then B #= 0
?- maplist(i_j_gt01, [1,2,3,2,1,3], [2,3,2,1,3,2], Bs).
Bs = [1,1,0,0,1,0].
Last, sum up [1,1,0,0,1,0] using sum/3:
?- sum([1,1,0,0,1,0], #=, N).
N = 3.
Let's put it all together!
count_adj_gt([E|Es], N) :-
last(Fs, _, [E|Es]), % or: `append(Fs, [_], [E|Es])`
% or: `list_butlast([E|Es], Fs)`
maplist(i_j_gt01, Es, Fs, Bs),
sum(Bs, #=, N).
Sample query using SICStus Prolog 4.3.2:
?- count_adj_gt([1,2,3,2,1,3,2], N).
N = 3. % succeeds deterministically
not sure about
an elegant terminating condition
my whole code would be
?- Vs=[1,2,3,2,1,3,2], aggregate_all(count, (append(_,[X,Y|_], Vs), X<Y), Count).
That's all...
If you need something more complex, remember that library(clpfd) has more to offer.

Excluding all occurrences of the minimum number in a list

As a Prolog newbie, I try to define a predicate filter_min/2 which takes two lists to determine if the second list is the same as the first, but with all occurrences of the minimum number removed.
Sample queries with expected results:
?- filter_min([3,2,7,8], N).
N = [3,7,8].
?- filter_min([3,2,7,8], [3,7,8]).
true.
I tried but I always get the same result: false. I don't know what the problem is. I need help!
Here is my code:
filter_min(X,Y) :-
X == [],
write("ERROR: List parameter is empty!"),
!;
min_list(X,Z),
filter(X,Y,Z).
filter([],[],0).
filter([H1|T1],[H2|T2],Z) :-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> H2 is H1, filter(T1,T2,Z);
filter(T1,T2,Z).
There are a couple of problems with your code:
filter([],[],0). will not unify when working with any list that does not have 0 as its minimum value, which is not what you want. You want it to unify regardless of the minimum value to end your recursion.
The way you wrote filter([H1|T1],[H2|T2],Z) and its body will make it so that the two lists always have the same number of elements, when in fact the second one should have at least one less.
A correct implementation of filter/3 would be the following:
filter([],[],_).
filter([H1|T1],L2,Z):-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> filter(T1,T2,Z), L2 = [H1|T2];
filter(T1,L2,Z).
A bounty was offered...
... for a pure solution that terminates for (certain) cases where neither the length of the first nor of the second argument is known.
Here's a candidate implementation handling integer values, built on clpfd:
:- use_module(library(clpfd)).
filter_min(Xs,Ys) :-
filter_min_picked_gt(Xs,_,false,Ys).
filter_min_picked_gt([] ,_,true ,[]).
filter_min_picked_gt([Z|Xs],M,Picked,[Z|Zs]) :-
Z #> M,
filter_min_picked_gt(Xs,M,Picked,Zs).
filter_min_picked_gt([M|Xs],M,_,Zs) :-
filter_min_picked_gt(Xs,M,true,Zs).
Some sample queries:
?- filter_min([3,2,7,8],[3,7,8]).
true ; false. % correct, but leaves choicepoint
?- filter_min([3,2,7,8],Zs).
Zs = [3,7,8] ; false. % correct, but leaves choicepoint
Now, some queries terminate even though both list lengths are unknown:
?- filter_min([2,1|_],[1|_]).
false. % terminates
?- filter_min([1,2|_],[3,2|_]).
false. % terminates
Note that the implementation doesn't always finitely fail (terminate) in cases that are logically false:
?- filter_min([1,2|_],[2,1|_]). % does _not_ terminate
For a Prolog newbie, better start with the basics. The following works when first argument is fully instantiated, and the second is an uninstantiated variable, computing the result in one pass over the input list.
% remmin( +From, -Result).
% remmin([],[]). % no min elem to remove from empty list
remmin([A|B], R):-
remmin(B, A, [A], [], R). % remove A from B to get R, keeping [A]
% in case a smaller elem will be found
remmin([C|B], A, Rev, Rem, R):-
C > A -> remmin(B, A, [C|Rev], [C|Rem], R) ;
C==A -> remmin(B, A, [C|Rev], Rem, R) ;
C < A -> remmin(B, C, [C|Rev], Rev, R).
remmin([], _, _, Rem, R) :- reverse(Rem, R).
First, we can get the minimum number using the predicate list_minnum/2:
?- list_minnum([3,2,7,8],M).
M = 2.
We can define list_minnum/2 like this:
list_minnum([E|Es],M) :-
V is E,
list_minnum0_minnum(Es,V,M).
list_minnum0_minnum([],M,M).
list_minnum0_minnum([E|Es],M0,M) :-
M1 is min(E,M0),
list_minnum0_minnum(Es,M1,M).
For the sake of completeness, here's the super-similar list_maxnum/2:
list_maxnum([E|Es],M) :-
V is E,
list_maxnum0_maxnum(Es,V,M).
list_maxnum0_maxnum([],M,M).
list_maxnum0_maxnum([E|Es],M0,M) :-
M1 is max(E,M0),
list_maxnum0_maxnum(Es,M1,M).
Next, we use meta-predicate tfilter/3 in tandem with dif/3 to exclude all occurrences of M:
?- M=2, tfilter(dif(M),[2,3,2,7,2,8,2],Xs).
Xs = [3,7,8].
Put the two steps together and define min_excluded/2:
min_excluded(Xs,Ys) :-
list_minnum(Xs,M),
tfilter(dif(M),Xs,Ys).
Let's run some queries!
?- min_excluded([3,2,7,8],Xs).
Xs = [3,7,8].
?- min_excluded([3,2,7,8,2],Xs).
Xs = [3,7,8].

How to find the number of inversions in a list in Prolog

As someone who's new to Prolog, I'm looking to find out what a good way to count the number of inversions in a list.
I know how to flatten a matrix using flatten(Matrix, FlatMatrix), thus creating a variable that contains a single set of elements in the matrix. However, I'm unsure as to how to go about finding the number of inversions in that list.
From my understanding, the number of inversions in a matrix of numbers from 0...n is the total number of elements that are less than the number being compared (please correct me if I'm wrong on this).
I have a tiny bit of understanding of how setof/3 works in Prolog but I'd love to know a more efficient way to tackle the figuring out of the number of inversions in a flattened matrix. Variables in Prolog are strange to me so simple explanations would be best.
Thank you in advance!
First, I didn't quite get the meaning of what you were calling "inversion", so I'll stick to the quasi-canonical interpretation that
#CapelliC used in his answer to this question.
Let's assume that all list items are integers, so we can use clpfd.
:- use_module(library(clpfd)).
z_z_order(X,Y,Op) :-
zcompare(Op,X,Y).
To count the number of inversions (up-down direction changes), we do the following four steps:
compare adjacent items (using mapadj/3, as defined at the very end of this answer)
?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_order,Zs,Cs0).
Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],
Cs0 = [ <,<,>,>,<,=,<,<,<,<,>,=,=,>,< ].
eliminate all occurrences of = in Cs0 (using
tfilter/3 and dif/3)
?- Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<], tfilter(dif(=),Cs0,Cs1).
Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<],
Cs1 = [<,<,>,>,<, <,<,<,<,>, >,<,<].
get runs of equal items in Cs1 (using
splitlistIfAdj/3 and dif/3)
?- Cs1 = [<,<,>,>,<,<,<,<,<,>,>,<,<], splitlistIfAdj(dif,Cs1,Cs).
Cs1 = [ <,< , >,> , <,<,<,<,< , >,> , <,< ],
Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]].
the number of inversions is one less than the number of runs (using
length/2 and (#=)/2)
?- Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], length(Cs,L), N #= max(0,L-1).
Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], L = 5, N = 4.
That's it. Let's put it all together!
zs_invcount(Zs,N) :-
mapadj(z_z_order,Zs,Cs0),
tfilter(dif(=),Cs0,Cs1),
splitlistIfAdj(dif,Cs1,Cs),
length(Cs,L),
N #= max(0,L-1).
Sample uses:
?- zs_invcount([1,2,3],0),
zs_invcount([1,2,3,2],1),
zs_invcount([1,2,3,3,2],1), % works with duplicate items, too
zs_invcount([1,2,3,3,2,1,1,1],1),
zs_invcount([1,2,3,3,2,1,1,1,4,6],2),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1],3),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1,1],3).
true.
Implementation of meta-predicate mapadj/3
:- meta_predicate mapadj(3,?,?), list_prev_mapadj_list(?,?,3,?).
mapadj(P_3,[A|As],Bs) :-
list_prev_mapadj_list(As,A,P_3,Bs).
list_prev_mapadj_list([] ,_ , _ ,[]).
list_prev_mapadj_list([A1|As],A0,P_3,[B|Bs]) :-
call(P_3,A0,A1,B),
list_prev_mapadj_list(As,A1,P_3,Bs).
Here's an alternative to my previous answer. It is based on clpfd and meta-predicate mapadj/3:
:- use_module(library(clpfd)).
Using meta-predicate tfilter/3, bool01_t/2, and clpfd sum/3 we define:
z_z_momsign(Z0,Z1,X) :-
X #= max(-1,min(1,Z1-Z0)).
z_z_absmomsign(Z0,Z1,X) :-
X #= min(1,abs(Z1-Z0)).
#\=(X,Y,Truth) :-
X #\= Y #<==> B,
bool01_t(B,Truth).
Finally, we define zs_invcount/2 like so:
zs_invcount(Zs,N) :-
mapadj(z_z_momsign,Zs,Ms0),
tfilter(#\=(0),Ms0,Ms),
mapadj(z_z_absmomsign,Ms,Ds),
sum(Ds,#=,N).
Sample use:
?- zs_invcount([1,2,3],0),
zs_invcount([1,2,3,2],1),
zs_invcount([1,2,3,3,2],1), % works with duplicate items, too
zs_invcount([1,2,3,3,2,1,1,1],1),
zs_invcount([1,2,3,3,2,1,1,1,4,6],2),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1],3),
zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1,1],3).
true.
Edit
Consider the execution of following sample query in more detail:
?- zs_invcount([1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],N).
Let's proceed step-by-step!
For all adjacent list items, calculate the sign of their "momentum":
?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_momsign,Zs,Ms0).
Zs = [1,2, 4,3, 2,3,3,4,5,6,7, 6,6,6, 5,8],
Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ].
Eliminate all sign values of 0:
?- Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1], tfilter(#\=(0),Ms0,Ms).
Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1],
Ms = [1,1,-1,-1,1, 1,1,1,1,-1, -1,1].
Get the "momentum inversions", i.e., absolute signs of the momentum of momentums.
?- Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1], mapadj(z_z_absmomsign,Ms,Ds).
Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1],
Ds = [ 0,1, 0, 1,0,0,0,0,1, 0, 1 ].
Finally, sum up the number of "momentum inversions" using sum/3:
?- Ds = [0,1,0,1,0,0,0,0,1,0,1], sum(Ds,#=,N).
N = 4, Ds = [0,1,0,1,0,0,0,0,1,0,1].
Or, alternatively, all steps at once:
:- Zs = [1,2,4, 3, 2,3,3,4,5,6,7, 6,6,6, 5,8], mapadj(z_z_momsign,Zs,Ms0),
Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ], tfilter(#\=(0),Ms0,Ms),
Ms = [ 1,1,-1,-1,1, 1,1,1,1,-1, -1,1 ], mapadj(z_z_absmomsign,Ms,Ds),
Ds = [ 0,1, 0, 1, 0, 0,0,0,1, 0, 1 ], sum(Ds,#=,N),
N = 4.
a possible definition, attempting to keep it as simple as possible:
count_inversions(L, N) :-
direction(L, D, L1),
count_inversions(L1, D, 0, N).
direction([A,B|L], D, [B|L]) :-
A > B -> D = down ; D = up.
count_inversions([_], _, N, N).
count_inversions(L, D, M, N) :-
direction(L, D, L1),
!, count_inversions(L1, D, M, N).
count_inversions(L, _, M, N) :-
direction(L, D1, L1),
M1 is M+1, count_inversions(L1, D1, M1, N).
The direction/3 predicate compares a pair of elements, determining if they are in ascending/descending order. Such information is passed down visiting the list, and if it cannot be matched, a counter is incremented (an accumulator, starting from 0). When the visit stops (the list has only 1 elements, then no direction can be determined), the accumulated counter is 'passed up' to be returned at the top level call.
I opted for a cut, instead of 'if/then/else' construct, so you can try to rewrite by yourself count_inversions/4 using it (you can see it used in direction/3). Beware of operators precedence!
note: direction/3 ignores the ambiguity inherent when A =:= B, and assigns 'up' to this case.
HTH