Finding the longest contiguous sublist in Prolog - list

I'm a beginner in Prolog and this is my question:
I have a sorted list of integers without duplicates i.e. [1,2,3,11,12,13,14,21,22,23,24,25]
I want to write a predicate that finds the longest contiguous sublist of the elements of the list, that is the longest list where each integer is followed by its subsequent integer (in the set of natural numbers).
In the above example this list would be [21,22,23,24,25] where length = 5.
In case there are more than one lists with the same maximum length, I'm interested in just one of them, no matter which.
It should work like this:
maxCont([1,2,3,11,12,13,14,21,22,23,24,25],Lst]).
Lst = [21,22,23,24,25].

First, we define z_nonsucc_t/3 based on clpfd and bool01_t/2:
:- use_module(library(clpfd)).
z_nonsucc_t(X,Y,T) :-
Y #\= X+1 #<==> B,
bool01_t(B,T).
To split an integer list into consecutive runs, we use splitlistIfAdj/3 like this:
?- splitlistIfAdj(z_nonsucc_t,[1,2,3,11,12,13,14,21,22,23,24,25],Pss).
Pss = [[1,2,3],[11,12,13,14],[21,22,23,24,25]].
Next, we define meta-predicate max_of_by/3 based on if_/3, (#>)/3, and (#>=)/3:
max_of_by(X,[E|Es],P_2) :-
call(P_2,E,V),
max_of_by_aux(Es,P_2,V,[E],Rs),
reverse(Rs,Xs),
member(X,Xs).
max_of_by_aux([] , _ ,_ ,Bs ,Bs).
max_of_by_aux([E|Es],P_2,V0,Bs0,Bs) :-
call(P_2,E,V),
if_(V #> V0, Bs1=[], Bs1=Bs0),
if_(V #>= V0, (V1 = V , Bs2 = [E|Bs1]),
(V1 = V0, Bs2 = Bs1 )),
max_of_by_aux(Es,P_2,V1,Bs2,Bs).
To get the longest list(s), we use meta-predicate max_of_by/3 with length/2 like so:
?- max_of_by(Xs,[[1,2,3],[11,12,13,14],[21,22,23,24,25]],length).
Xs = [21,22,23,24,25].
Note that max_of_by/3 may succeed more than once in tie cases:
?- max_of_by(Xs,[[1,2,3],[11,12,13,14,15],[21,22,23,24,25]],length).
Xs = [11,12,13,14,15]
; Xs = [21,22,23,24,25].
Put it all together in predicate maxCont/2:
maxCont(Zs,Xs) :-
splitlistIfAdj(z_nonsucc_t,Zs,Pss),
max_of_by(Xs,Pss,length).
Sample queries:
?- maxCont([1,2,3,11,12,13,14, 21,22,23,24,25],Xs).
Xs = [21,22,23,24,25].
?- maxCont([1,2,3,11,12,13,14,15,21,22,23,24,25],Xs).
Xs = [11,12,13,14,15]
; Xs = [21,22,23,24,25].

Related

Prolog - first list is sublist of second list while maintaining order?

I want to check if elements of list L1 occur consecutively, and in the same order, in list L2.
For example - check([b,c],[a,b,c,d]) must return true while check([b,d],[a,b,c,d]) must return false
I looked at similar posts Prolog - first list is sublist of second list? and also tried out similar solutions but whenever i try to check if elements are present, i am unable to check if ordering is consecutive
check( [], _ ).
check( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
check( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
and if i try to check if ordering is correct then my code is breaking.
check( [], _ ).
check( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
Interesting problem! I'm surprised at how much code it took, so there may be a better solution than this.
First we need a helper to insist that a list is a prefix of another list. The base case is that we ran out of a prefix list; the inductive case is that the current items match and the remainder of both lists is a prefix match.
prefix([X|Xs], [X|Ys]) :- prefix(Xs, Ys).
prefix([], _).
Now finding a consecutive sublist amounts to searching down a list for prefix matches. If the current items match, then having a prefix is a match:
consecutive_sublist([X|Xs], [X|Ys]) :- prefix(Xs, Ys).
Otherwise, we just discard this element of the search target and try again on the sublist:
consecutive_sublist(Prefix, [_|Ys]) :- consecutive_sublist(Prefix, Ys).
We can make use of append/2 [swi-doc] to write this with a one-liner:
subsequence(X, Y) :-
append([_,X,_], Y).
or we can implement a subsequence/4 that will unify two variables Prefix and Suffix with the list before and after the subsequence:
subsequence(X, Y, Prefix, Suffix) :-
append([Prefix,X,Suffix], Y).
Here we thus have two don't care variables that will collect the prefix and suffix before and after the subsequence.
An alternative solution using the de facto standard definition of the append/3 predicate:
check(SubList, List) :-
append(Prefix, _, List),
append(_, SubList, Prefix).
Sample calls:
| ?- check([b,d],[a,b,c,d]).
no
| ?- check([b,c],[a,b,c,d]).
true ? ;
no
| ?- check([b,c],[a,b,c,d,b,c,f]).
true ? ;
true ? ;
no
We can also use this definition to generate sublist-list pairs:
| ?- check(SubList, List).
SubList = [] ? ;
List = [A|_]
SubList = [A] ? ;
List = [_|_]
SubList = [] ? ;
List = [A,B|_]
SubList = [A,B] ? ;
List = [_,A|_]
SubList = [A] ? ;
List = [_,_|_]
SubList = [] ? ;
List = [A,B,C|_]
SubList = [A,B,C] ? ;
List = [_,A,B|_]
SubList = [A,B] ? ;
...
This problem also gives you the opportunity to learn about termination properties of predicates. As an experiment, exchange the order of the append/3 calls and then check what happens on backtracking for e.g. the two first sample calls.

Prolog - How to remove N number of members from a list

So I'm making a predicate called removeN(List1, N, List2). It should basically function like this:
removeN([o, o, o, o], 3, List2).
List2 = [o].
The first argument is a list with a number of the same members ([o, o, o] or [x, x, x]). The second argument is the number of members you wanna remove, and the third argument is the list with the removed members.
How should I go about this, I was thinking about using length of some sort.
Thanks in advance.
Another approach would be to use append/3 and length/2:
remove_n(List, N, ShorterList) :-
length(Prefix, N),
append(Prefix, ShorterList, List).
Think about what the predicate should describe. It's a relation between a list, a number and a list that is either equal to the first or is missing the specified number of the first elements. Let's pick a descriptive name for it, say list_n_removed/3. Since you want a number of identical elements to be removed, let's keep the head of the list for comparison reasons, so list_n_removed/3 is just the calling predicate and another predicate with and additional argument, let's call it list_n_removed_head/4, describes the actual relation:
list_n_removed([X|Xs],N,R) :-
list_n_removed_head([X|Xs],N,R,X).
The predicate list_n_removed_head/4 has to deal with two distinct cases: either N=0, then the first and the third argument are the same list or N>0, then the head of the first list has to be equal to the reference element (4th argument) and the relation has to hold for the tail as well:
list_n_removed_head(L,0,L,_X).
list_n_removed_head([X|Xs],N,R,X) :-
N>0,
N0 is N-1,
list_n_removed_head(Xs,N0,R,X).
Now let's see how it works. Your example query yields the desired result:
?- list_n_removed([o,o,o,o],3,R).
R = [o] ;
false.
If the first three elements are not equal the predicate fails:
?- list_n_removed([o,b,o,o],3,R).
false.
If the length of the list equals N the result is the empty list:
?- list_n_removed([o,o,o],3,R).
R = [].
If the length of the list is smaller than N the predicate fails:
?- list_n_removed([o,o],3,R).
false.
If N=0 the two lists are identical:
?- list_n_removed([o,o,o,o],0,R).
R = [o, o, o, o] ;
false.
If N<0 the predicate fails:
?- list_n_removed([o,o,o,o],-1,R).
false.
The predicate can be used in the other direction as well:
?- list_n_removed(L,0,[o]).
L = [o] ;
false.
?- list_n_removed(L,3,[o]).
L = [_G275, _G275, _G275, o] ;
false.
However, if the second argument is variable:
?- list_n_removed([o,o,o,o],N,[o]).
ERROR: >/2: Arguments are not sufficiently instantiated
This can be avoided by using CLP(FD). Consider the following changes:
:- use_module(library(clpfd)). % <- new
list_n_removed([X|Xs],N,R) :-
list_n_removed_head([X|Xs],N,R,X).
list_n_removed_head(L,0,L,_X).
list_n_removed_head([X|Xs],N,R,X) :-
N #> 0, % <- change
N0 #= N-1, % <- change
list_n_removed_head(Xs,N0,R,X).
Now the above query delivers the expected result:
?- list_n_removed([o,o,o,o],N,[o]).
N = 3 ;
false.
As does the most general query:
?- list_n_removed(L,N,R).
L = R, R = [_G653|_G654],
N = 0 ;
L = [_G653|R],
N = 1 ;
L = [_G26, _G26|R],
N = 2 ;
L = [_G26, _G26, _G26|R],
N = 3 ;
.
.
.
The other queries above yield the same answers with the CLP(FD) version.
Alternative solution using foldl/4:
remove_step(N, _Item, Idx:Tail, IdxPlusOne:Tail) :-
Idx < N, succ(Idx, IdxPlusOne).
remove_step(N, Item, Idx:Tail, IdxPlusOne:NewTail) :-
Idx >= N, succ(Idx, IdxPlusOne),
Tail = [Item|NewTail].
remove_n(List1, N, List2) :-
foldl(remove_step(N), List1, 0:List2, _:[]).
The idea here is to go through the list while tracking index of current element. While element index is below specified number N we essentially do nothing. After index becomes equal to N, we start building output list by appending all remaining elements from source list.
Not effective, but you still might be interested in the solution, as it demonstrates usage of a very powerful foldl predicate, which can be used to solve wide range of list processing problems.
Counting down should work fine
removeN([],K,[]) :- K>=0.
removeN(X,0,X).
removeN([_|R],K,Y) :- K2 is K-1, removeN(R,K2,Y).
This works for me.
I think this is the easiest way to do this.
trim(L,N,L2). L is the list and N is number of elements.
trim(_,0,[]).
trim([H|T],N,[H|T1]):-N1 is N-1,trim(T,N1,T1).

Create list of pair of values 0 to n-1 in lexicographical order

I'm doing a program with Result is a pair of values [X,Y] between 0 and N-1 in lexicographic order
I have this right now:
pairs(N,R) :-
pairsHelp(N,R,0,0).
pairsHelp(N,[],N,N) :- !.
pairsHelp(N,[],N,0) :- !.
pairsHelp(N,[[X,Y]|List],X,Y) :-
Y is N-1,
X < N,
X1 is X + 1,
pairsHelp(N,List,X1,0).
pairsHelp(N,[[X,Y]|List],X,Y) :-
Y < N,
Y1 is Y + 1,
pairsHelp(N,List,X,Y1).
I'm getting what I want the first iteration but Prolog keeps going and then gives me a second answer.
?-pairs(2,R).
R = [[0,0],[0,1],[1,0],[1,1]] ;
false.
I don't want the second answer (false), just the first. I want it to stop after it finds the answer. Any ideas?
Keep in mind that there is a much easier way to get what you are after. If indeed both X and Y are supposed to be integers, use between/3 to enumerate integers ("lexicographical" here is the same as the order of natural numbers: 0, 1, 2, .... This is the order in which between/3 will enumerate possible solutions if the third argument is a variable):
pairs(N, R) :-
succ(N0, N),
bagof(P, pair(N0, P), R).
pair(N0, X-Y) :-
between(0, N0, X),
between(0, N0, Y).
And then:
?- pairs(2, R).
R = [0-0, 0-1, 1-0, 1-1].
?- pairs(3, R).
R = [0-0, 0-1, 0-2, 1-0, 1-1, 1-2, 2-0, 2-1, ... - ...].
I am using the conventional Prolog way of representing a pair, X-Y (in canonical form: -(X, Y)) instead of [X,Y] (canonical form: .(X, .(Y, []))).
The good thing about this program is that you can easily re-write it to work with another "alphabet" of your choosing.
?- between(0, Upper, X).
is semantically equivalent to:
x(0).
x(1).
% ...
x(Upper).
?- x(X).
For example, if we had an alphabet that consists of b, a, and c (in that order!):
foo(b).
foo(a).
foo(c).
foo_pairs(Ps) :-
bagof(X-Y, ( foo(X), foo(Y) ), Ps).
and then:
?- foo_pairs(R).
R = [b-b, b-a, b-c, a-b, a-a, a-c, c-b, c-a, ... - ...].
The order of the clauses of foo/1 defines the order of your alphabet. The conjunction foo(X), foo(Y) together with the order of X-Y in the pair defines the order of pairs in the list. Try writing for example bagof(X-Y, ( foo(Y), foo(X) ), Ps) to see what will be the order of pairs in Ps.
Use dcg in combination with lambda!
?- use_module(library(lambda)).
In combination with meta-predicate init0/3 and
xproduct//2 ("cross product") simply write:
?- init0(=,3,Xs), phrase(xproduct(\X^Y^phrase([X-Y]),Xs),Pss).
Xs = [0,1,2], Pss = [0-0,0-1,0-2,1-0,1-1,1-2,2-0,2-1,2-2].
How about something a little more general? What about other values of N?
?- init0(=,N,Xs), phrase(xproduct(\X^Y^phrase([X-Y]),Xs),Pss).
N = 0, Xs = [], Pss = []
; N = 1, Xs = [0], Pss = [0-0]
; N = 2, Xs = [0,1], Pss = [0-0,0-1,
1-0,1-1]
; N = 3, Xs = [0,1,2], Pss = [0-0,0-1,0-2,
1-0,1-1,1-2,
2-0,2-1,2-2]
; N = 4, Xs = [0,1,2,3], Pss = [0-0,0-1,0-2,0-3,
1-0,1-1,1-2,1-3,
2-0,2-1,2-2,2-3,
3-0,3-1,3-2,3-3]
; N = 5, Xs = [0,1,2,3,4], Pss = [0-0,0-1,0-2,0-3,0-4,
1-0,1-1,1-2,1-3,1-4,
2-0,2-1,2-2,2-3,2-4,
3-0,3-1,3-2,3-3,3-4,
4-0,4-1,4-2,4-3,4-4]
...
Does it work for other terms, too? What about order? Consider a case #Boris used in his answer:
?- phrase(xproduct(\X^Y^phrase([X-Y]),[b,a,c]),Pss).
Pss = [b-b,b-a,b-c,a-b,a-a,a-c,c-b,c-a,c-c]. % succeeds deterministically

How can I compare two lists in prolog, returning true if the second list is made of every other element of list one?

I would solve it by comparing the first index of the first list and adding 2 to the index. But I do not know how to check for indexes in prolog.
Also, I would create a counter that ignores what is in the list when the counter is an odd number (if we start to count from 0).
Can you help me?
Example:
everyOther([1,2,3,4,5],[1,3,5]) is true, but everyOther([1,2,3,4,5],[1,2,3]) is not.
We present three logically-pure definitions even though you only need one—variatio delectat:)
Two mutually recursive predicates list_oddies/2 and skipHead_oddies/2:
list_oddies([],[]).
list_oddies([X|Xs],[X|Ys]) :-
skipHead_oddies(Xs,Ys).
skipHead_oddies([],[]).
skipHead_oddies([_|Xs],Ys) :-
list_oddies(Xs,Ys).
The recursive list_oddies/2 and the non-recursive list_headless/2:
list_oddies([],[]).
list_oddies([X|Xs0],[X|Ys]) :-
list_headless(Xs0,Xs),
list_oddies(Xs,Ys).
list_headless([],[]).
list_headless([_|Xs],Xs).
A "one-liner" which uses meta-predicate foldl/4 in combination with Prolog lambdas:
:- use_module(library(lambda)).
list_oddies(As,Bs) :-
foldl(\X^(I-L)^(J-R)^(J is -I,( J < 0 -> L = [X|R] ; L = R )),As,1-Bs,_-[]).
All three implementations avoid the creation of useless choicepoints, but they do it differently:
#1 and #2 use first-argument indexing.
#3 uses (->)/2 and (;)/2 in a logically safe way—using (<)/2 as the condition.
Let's have a look at the queries #WouterBeek gave in his answer!
?- list_oddies([],[]),
list_oddies([a],[a]),
list_oddies([a,b],[a]),
list_oddies([a,b,c],[a,c]),
list_oddies([a,b,c,d],[a,c]),
list_oddies([a,b,c,d,e],[a,c,e]),
list_oddies([a,b,c,d,e,f],[a,c,e]),
list_oddies([a,b,c,d,e,f,g],[a,c,e,g]),
list_oddies([a,b,c,d,e,f,g,h],[a,c,e,g]).
true. % all succeed deterministically
Thanks to logical-purity, we get logically sound answers—even with the most general query:
?- list_oddies(Xs,Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_B], Ys = [_A]
; Xs = [_A,_B,_C], Ys = [_A,_C]
; Xs = [_A,_B,_C,_D], Ys = [_A,_C]
; Xs = [_A,_B,_C,_D,_E], Ys = [_A,_C,_E]
; Xs = [_A,_B,_C,_D,_E,_F], Ys = [_A,_C,_E]
; Xs = [_A,_B,_C,_D,_E,_F,_G], Ys = [_A,_C,_E,_G]
; Xs = [_A,_B,_C,_D,_E,_F,_G,_H], Ys = [_A,_C,_E,_G]
...
There are two base cases and one recursive case:
From an empty list you cannot take any odd elements.
From a list of length 1 the only element it contains is an odd element.
For lists of length >2 we take the first element but not the second one; the rest of the list is handled in recursion.
The code looks as follows:
odd_ones([], []).
odd_ones([X], [X]):- !.
odd_ones([X,_|T1], [X|T2]):-
odd_ones(T1, T2).
Notice that in Prolog we do not need to maintain an explicit index that has to be incremented etc. We simply use matching: [] matches the empty list, [X] matches a singleton list, and [X,_|T] matches a list of length >2. The | separates the first two elements in the list from the rest of the list (called the "tail" of the list). _ denotes an unnamed variable; we are not interested in even elements.
Also notice the cut (!) which removes the idle choicepoint for the second base case.
Example of use:
?- odd_ones([], X).
X = [].
?- odd_ones([a], X).
X = [a].
?- odd_ones([a,b], X).
X = [a].
?- odd_ones([a,b,c], X).
X = [a, c].
?- odd_ones([a,b,c,d], X).
X = [a, c].
?- odd_ones([a,b,c,d,e], X).
X = [a, c, e].

Repeated elements in a list in Prolog

I would like to find a method to find the most repeated element in a list if two elements repeat the same number of times. I want the predicate to be a list that contains both elements. How can I do that?
Sample queries and expected answers:
?- maxRepeated([1,3,3,4,2,2],X).
X = [3,2].
% common case: there is one element that is the most repeated
?- maxRepeated([1,3,3,3,3,4,2,2],X).
X = [3].
% all elements repeat the same number of times
?- maxRepeated([1,3,4,2],X).
X = [1,3,4,2].
I have the same problem with the less repeated element.
The predicate mostcommonitems_in/2 (to be presented in this answer) bears more than a little resemblance to
mostcommonitem_in/2, defined in one of my previous answers.
In the following we use list_counts/2, Prolog lambdas, foldl/4, tchoose/3, and (=)/3:
:- use_module(library(lambda)).
mostcommonitems_in(Ms,Xs) :-
list_counts(Xs,Cs),
foldl(\ (_-N)^M0^M1^(M1 is max(M0,N)),Cs,0,M),
tchoose(\ (E-N)^E^(N=M), Cs,Ms).
Let's run some queries!
First, the three queries given by the OP:
?- mostcommonitems_in(Xs,[1,3,3,4,2,2]).
Xs = [3,2].
?- mostcommonitems_in(Xs,[1,3,3,3,3,4,2,2]).
Xs = [3].
?- mostcommonitems_in(Xs,[1,3,4,2]).
Xs = [1,3,4,2].
Alright! Some more ground queries---hat tip to #lurker and #rpax:
?- mostcommonitems_in(Xs,[1,3,2,1,3,3,1,4,1]).
Xs = [1].
?- mostcommonitems_in(Xs,[1,3,3,4,3,2]).
Xs = [3].
?- mostcommonitems_in(Xs,[1,2,3,4,5,6]).
Xs = [1,2,3,4,5,6].
?- mostcommonitems_in(Xs,[1,3,3,4,2,3,2,2]).
Xs = [3,2].
OK! How about three items each of which occurs exactly three times in the list?
?- mostcommonitems_in(Xs,[a,b,c,a,b,c,a,b,c,x,d,e]).
Xs = [a,b,c]. % works as expected
How about the following somewhat more general query?
?- mostcommonitems_in(Xs,[A,B,C]).
Xs = [C] , A=B , B=C
; Xs = [B] , A=B , dif(B,C)
; Xs = [C] , A=C , dif(B,C)
; Xs = [C] , dif(A,C), B=C
; Xs = [A,B,C], dif(A,B), dif(A,C), dif(B,C).
Above query breaks almost all impure codes... Our Prolog code is pure, so we're good to go!
I don't know too much about prolog, and probably there's a way to do this better, but here's a working solution: (SWI prolog)
%List of tuples, keeps track of the number of repetitions.
modify([],X,[(X,1)]).
modify([(X,Y)|Xs],X,[(X,K)|Xs]):- K is Y+1.
modify([(Z,Y)|Xs],X,[(Z,Y)|K]):- Z =\= X, modify(Xs,X,K).
highest((X1,Y1),(_,Y2),(X1,Y1)):- Y1 >= Y2.
highest((_,Y1),(X2,Y2),(X2,Y2)):- Y2 > Y1.
maxR([X],X).
maxR([X|Xs],K):- maxR(Xs,Z),highest(X,Z,K).
rep([],R,R).
rep([X|Xs],R,R1):-modify(R,X,R2),rep(Xs,R2,R1).
maxRepeated(X,R):- rep(X,[],K),maxR(K,R).
?- maxRepeated([1,3,3,4,3,2] ,X).
X = (3, 3) .
?- maxRepeated([1,2,3,4,5,6] ,X).
X = (1, 1) .
The less repeated element is analogous.
I think that is better to use tuples in this case, but changing the result into a list shouldn't be a problem.
There is my solution on Visual Prolog:
domains
value=integer
tuple=t(value,integer)
list=value*
tuples=tuple*
predicates
modify(tuples,value,tuples)
highest(tuple,tuple,tuple)
maxR(tuples,integer,integer)
maxR(tuples,integer)
rep(list,tuples,tuples)
maxRepeated(list,list)
filter(tuples,integer,list)
clauses
modify([],X,[t(X,1)]):- !.
modify([t(X,Y)|Xs],X,[t(X,K)|Xs]):- K = Y+1, !.
modify([t(Z,Y)|Xs],X,[t(Z,Y)|K]):- Z <> X, modify(Xs,X,K).
highest(t(X1,Y1),t(_,Y2),t(X1,Y1)):- Y1 >= Y2, !.
highest(t(_,Y1),t(X2,Y2),t(X2,Y2)):- Y2 > Y1.
maxR([],R,R):- !.
maxR([t(_,K)|Xs],Rs,R):- K>Rs,!, maxR(Xs,K,R).
maxR([_|Xs],Rs,R):- maxR(Xs,Rs,R).
maxR(X,R):- maxR(X,0,R).
rep([],R,R).
rep([X|Xs],R,R1):-modify(R,X,R2),rep(Xs,R2,R1).
filter([],_,[]):-!.
filter([t(X,K)|Xs],K,[X|FXs]):- !, filter(Xs,K,FXs).
filter([_|Xs],K,FXs):- filter(Xs,K,FXs).
maxRepeated(X,RL):- rep(X,[],Reps),maxR(Reps,K),filter(Reps,K,RL).
goal
maxRepeated([1,3,3,4,2,3,2,2] ,X),
maxRepeated([1,2,3,4,5,6] ,Y).