Prolog: List of sublists - prolog

i have a task to create a list of sublists where the elements are sorted in consecutive order. I'm having difficulties because when it iterates through the list and returns multiple lists with a list, thats not what i want. My goal is a list with multiple sublists of length 3.
example
list([]).
list([_|T]) :- list(T).
sublist(L, []) :- list(L).
sublist([HX|TX],[HX|TY]) :- sublist(TX,TY).
sublist([_|TY], X) :- X = [_|_], sublist(TY, X).
This prints out every single sublist.
?- sublist([10,20,30,a,b], L).
L = [] ;
L = [10] ;
L = [10, 20] ;
L = [10, 20, 30] ;
L = [10, 20, 30, a] ;
L = [10, 20, 30, a, b] ;
L = [10, 20, 30, b] ;
..and so on
What i want is something like this
?- sublist([10,20,30,a,b], L).
L = [[10,20,30],[20,30,a],[30,a,b]]
I've been overthinking this i guess, and other thing X = [_,_,_] destroys my functionality.

You can use append/3 to obtain all the "sliding" sublists.
For the specific case of 3 elements:
sublist(L, LSubL):-
findall([A,B,C], append(_, [A,B,C|_], L), LSubL).
For the more general case of sliding window of 'Size' items:
sliding_window(L, Size, LSubL):-
length(SubL, Size),
append(SubL, _, SubLO),
findall(SubL, append(_, SubLO, L), LSubL).

Related

Unique sets that add up to target value n

The given database:
age(ann, 20).
age(joe, 44).
age(bob, 40).
age(min, 27).
age(cai, 20).
age(ned, 27).
age(deb, 36).
age(pat, 36).
age(edo, 24).
age(tod, 56).
What I have so far:
hundred(L) :-
findall(X, age(X,_), L1),
sum(L1,100,L),
length(L,Len),
Len > 1.
sum(_, 0, []).
sum(L, S, [H|T]) :-
select(H, L, L1),
age(H, A),
S1 is S-A,
S1 >= 0,
sum(L1, S1, T).
I'd like to get the unique sets of persons (of length > 1) in the database whose ages add up to 100. My solution yields all possible permutations whereas I'd like to get the unique sets of persons as below (the listing order in the output example is irrelevant):
L=[joe,tod];
L=[ann,joe,deb];
L=[ann,joe,pat];
L=[ann, edo, tod];
L=[ann, cai, deb, edo];
L=[ann, cai, edo, pat];
L=[cai, edo, tod];
L=[joe, cai, deb];
L=[joe, cai, pat];
L=[bob, deb, edo];
L=[bob, pat, edo];
For clarity: people may have the same age, and that's fine. So the number combinations themselves may include duplicates to get to 100, as long as those ages belong to different persons.
In the end, each solution should be a combination of different persons but should not be a mere permutation of a previous solution.
My solution always takes combinations of different persons, but lists every change in the order as a new solution.
I've tried to get rid of the permutations by putting all solutions in a list of lists and then sorting it to remove duplicates, but it didn't work.
For the numbers, can use generic predicates:
sub_list_sum(Sum, Lst, Sub) :-
compare(C, Sum, 0),
sub_list_sum_(C, Sum, Lst, Sub).
sub_list_sum_(=, 0, _, []).
sub_list_sum_(>, Sum, Lst, [E|Sub]) :-
select_forward(E, Lst, Lst0),
Sum0 is Sum - E,
compare(C, Sum0, 0),
sub_list_sum_(C, Sum0, Lst0, Sub).
% A subsequence variant of select/3
select_forward(E, [H|T], F) :-
select_forward_(T, H, E, F).
select_forward_(T, H, H, T).
select_forward_([H|T], _, E, F) :-
select_forward_(T, H, E, F).
Result in swi-prolog:
?- Dupes = [20, 44, 40, 27, 20, 27, 36, 36, 24, 56],
sort(Dupes, Sorted), sub_list_sum(100, Sorted, S).
S = [20, 24, 56] ;
S = [20, 36, 44] ;
S = [24, 36, 40] ;
S = [44, 56] ;
false.
Customising this for the ages:
age(ann, 20).
age(joe, 44).
age(bob, 40).
age(min, 27).
age(cai, 20).
age(ned, 27).
age(deb, 36).
age(pat, 36).
age(edo, 24).
age(tod, 56).
sub_list_age_sum(Sum, Sub) :-
% Want at least length 2
Sub = [_,_|_],
bagof(age(P, A), age(P, A), Ages),
compare(C, Sum, 0),
sub_list_age_sum_(C, Sum, Ages, Sub).
sub_list_age_sum_(=, 0, _, []).
sub_list_age_sum_(>, Sum, Lst, [P|Sub]) :-
select_forward(age(P, A), Lst, Lst0),
Sum0 is Sum - A,
compare(C, Sum0, 0),
sub_list_age_sum_(C, Sum0, Lst0, Sub).
... and add select_forward code as above - result:
?- sub_list_age_sum(100, L).
L = [ann, joe, deb] ;
L = [ann, joe, pat] ;
L = [ann, cai, deb, edo] ;
L = [ann, cai, pat, edo] ;
L = [ann, edo, tod] ;
L = [joe, cai, deb] ;
L = [joe, cai, pat] ;
L = [joe, tod] ;
L = [bob, deb, edo] ;
L = [bob, pat, edo] ;
L = [cai, edo, tod] ;
false.
Turns out that my initial idea actually works. The only thing needed is to sort each valid group before adding it to the list of lists, so that setof/3 can then recognize the permutations as duplicates and remove them, thus producing a list with unique groups.
Final solution:
hundred(G):-
findall(age(N,A),age(N,A),Ps),
setof(Sorted,
Ns^(sum(100,Ps,Ns),sort(Ns,Sorted)),
Groups),
member(G,Groups), G=[_,_|_].
sum(0,_,[]).
sum(Sum,Ps,[N|Ns]):-
select(age(N,A),Ps,Rem),
NewSum is Sum-A, NewSum >= 0,
sum(NewSum,Rem,Ns).

PROLOG : Given a number X, find all multiples of 4 less or equal than X

I have the following program:
list4(N, L) :-
list4(0, N, L).
list4(N, N, [N]).
list4(N0, N, [N0| List]) :-
N0 < N,
N1 is N0+1,
list4(N1, N, List).
When i change the line N1 is N0+1 to N1 is 4*N0 i get stack limit exceeded error.
My expected result is
list4(10,L).
L = [4,8]
Two problems with the 4*N0 version:
In list4/2 you initialize list4/3 with N0=0, which, multiplied with anything, always stays 0. That causes the infinite recursion. As you want multiples of four, you can just keep increasing N0 by one in each step and multiply it by 4 before putting it into the list.
The anchor relies on your count variable arriving at N exactly, but N0 * 4 overshoots. The first rule of list4/3 has to cover the rest of the cases with N0*4 > N as well. To also include the upper limit in the result, we can make the anchor stop at values larger than the limit and handle the upper bound itself in the second rule.
Expressed in code:
list4(N, L) :-
list4(1, N, L).
list4(N0, N, []) :- N4Times is N0*4,N4Times > N.
list4(N0, N, [N4Times| List]) :-
N4Times is N0*4,
N4Times =< N,
N1 is N0 + 1,
list4(N1, N, List).
Results in:
?- list4(10,L).
L = [4, 8] ;
false.
?- list4(8,L).
L = [4, 8] ;
false.
You can initialize the accumulator with 0 and increment it by 4 at each step.
% list4(+Upperbound, -MultiplesOfFour)
list4(U, M) :-
list4(0, U, M).
list4(A, U, L) :-
M is A+4,
( M =< U
-> L = [M|Ms],
list4(M, U, Ms)
; L = [] ).
Examples:
?- list4(10, L).
L = [4, 8].
?- list4(8, L).
L = [4, 8].
?- list4(20, L).
L = [4, 8, 12, 16, 20].
?- list4(30, L).
L = [4, 8, 12, 16, 20, 24, 28].
?- list4(3, L).
L = [].

Calculate whether the sum of exactly three values in a list is equal to N

Examples: ([1,2,3,7,6,9], 6). should print True, as 1+2+3=6.
([1,2,3,7,6,9], 5). should print False as there are no three numbers whose sum is 5.
([],N) where N is equal to anything should be false.
Need to use only these constructs:
A single clause must be defined (no more than one clause is allowed).
Only the following is permitted:
+, ,, ;, ., !, :-, is, Lists -- Head and Tail syntax for list types, Variables.
I have done a basic coding as per my understanding.
findVal([Q|X],A) :-
[W|X1]=X,
[Y|X2]=X,
% Trying to append the values.
append([Q],X1,X2),
% finding sum.
RES is Q+W+Y,
% verify here.
(not(RES=A)->
% finding the values.
(findVal(X2,A=)->
true
;
(findVal(X,A)->
% return result.
true
;
% return value.
false))
;
% return result.
true
).
It does not seem to run throwing the following error.
ERROR:
Undefined procedure: findVal/2 (DWIM could not correct goal)
Can someone help with this?
You can make use of append/3 [swi-doc] here to pick an element from a list, and get access to the rest of the elements (the elements after that element). By applying this technique three times, we thus obtain three items from the list. We can then match the sum of these elements:
sublist(L1, S) :-
append(_, [S1|L2], L1),
append(_, [S2|L3], L2),
append(_, [S3|_], L3),
S is S1 + S2 + S3.
Well, you can iterate (via backtracking) over all the sublists of 3 elements from the input list and see which ones sum 3:
sublist([], []).
sublist([H|T], [H|S]) :- sublist(T, S).
sublist([_|T], S) :- sublist(T, S).
:- length(L, 3), sublist([1,2,3,7,6,9], L), sum_list(L, 6).
I'm giving a partial solution here because it is an interesting problem even though the constraints are ridiculous.
First, I want something like select/3, except that will give me the tail of the list rather than the list without the item:
select_from(X, [X|R], R).
select_from(X, [_|T], R) :- select_from(X, T, R).
I want the tail, rather than just member/2, so I can recursively ask for items from the list without getting duplicates.
?- select_from(X, [1,2,3,4,5], R).
X = 1,
R = [2, 3, 4, 5] ;
X = 2,
R = [3, 4, 5] ;
X = 3,
R = [4, 5] ;
X = 4,
R = [5] ;
X = 5,
R = [] ;
false.
Yeah, this is good. Now I want to build a thing to give me N elements from a list. Again, I want combinations, because I don't want unnecessary duplicates if I can avoid it:
select_n_from(1, L, [X]) :- select_from(X, L, _).
select_n_from(N, L, [X|R]) :-
N > 1,
succ(N0, N),
select_from(X, L, Next),
select_n_from(N0, Next, R).
So the idea here is simple. If N = 1, then just do select_from/3 and give me a singleton list. If N > 1, then get one item using select_from/3 and then recur with N-1. This should give me all the possible combinations of items from this list, without giving me a bunch of repetitions I don't care about because addition is commutative and associative:
?- select_n_from(3, [1,2,3,4,5], R).
R = [1, 2, 3] ;
R = [1, 2, 4] ;
R = [1, 2, 5] ;
R = [1, 3, 4] ;
R = [1, 3, 5] ;
R = [1, 4, 5] ;
R = [2, 3, 4] ;
R = [2, 3, 5] ;
R = [2, 4, 5] ;
R = [3, 4, 5] ;
false.
We're basically one step away now from the result, which is this:
sublist(List, N) :-
select_n_from(3, List, R),
sumlist(R, N).
I'm hardcoding 3 here because of your problem, but I wanted a general solution. Using it:
?- sublist([1,2,3,4,5], N).
N = 6 ;
N = 7 ;
N = 8 ;
N = 8 ;
N = 9 ;
N = 10 ;
N = 9 ;
N = 10 ;
N = 11 ;
N = 12 ;
false.
You can also check:
?- sublist([1,2,3,4,5], 6).
true ;
false.
?- sublist([1,2,3,4,5], 5).
false.
?- sublist([1,2,3,4,5], 8).
true ;
true ;
false.
New users of Prolog will be annoyed that you get multiple answers here, but knowing that there are multiple ways to get 8 is probably interesting.

Prolog insert into a list

I would advice about this exercise:
Write a method insert, which has 3 parameters, the first an ordered
list, the second an int and the third an ordered list without repeated
values equal as the first one but containing the second parameter.
Example:
> insert([5, 6, 30, 60, 90], 40, L)
L = [5, 6, 30, 40, 60, 90]
> insert([5, 6, 30, 60, 90], 30, L)
L = [5, 6, 30, 60, 90]
I would do:
insert([],_,[_]).
insert([H],_,Result) :-
Result < H,
insert([],[],[Result|H]).
insert([H],_,Result) :-
Result > H,
insert([],[],[H|Result]).
insert([H,Y|Rest], _, Result):-
_ < Y,
insert([X|Rest], [], Result).
insert([H,Y|Rest], _, Result):-
_ > Y,
insert([Y|Rest], [], Result).
But I think base case when there is only one element is redundant and not needed because of we have the general recursive case and the empty list one. I need some suggest to improve or better explanations to polish the code.
Thank you for your time.
Try with compare:
:- use_module(library(clpfd)).
insert([], X, [X]).
insert([X|Xs], New, Ys) :-
zcompare(Order, X, New),
insert(Order, X, New, Xs, Ys).
insert(>, X, New, Xs, [New,X|Xs]).
insert(=, X, _, Xs, [X|Xs]).
insert(<, X, New, Xs, [X|Ys]) :-
insert(Xs, New, Ys).
but maybe you need explanation? It is strange, because you could also just read documentation as I did and find why this is good enough implementation, but of course maybe it is good to explain more, just in case.
insert([], X, [X]).
When first argument is empty list, second argument is the only element of the result list.
insert([X|Xs], New, Ys) :-
zcompare(Order, X, New), ...
When first argument is list with at least one element, take head element and compare it to New element. After compare or zcompare first argument Order is either > or = or < (but what do these mean? maybe guess or maybe even read documentation if it is not too much work).
insert(Order, X, New, Xs, Ys).
After comparing take the Order and the rest of the variables and....
insert(>, X, New, Xs, [New,X|Xs]).
Element at head of list is larger than New element. This means that result list should be New element followed by head followed by rest of list.
insert(=, X, _, Xs, [X|Xs]).
Element at head of list is exactly the same as New element. We are done, no need to insert anything just keep original list as result.
insert(<, X, New, Xs, [X|Ys]) :-
insert(Xs, New, Ys).
Element at head of list is smaller than New element: New element must come after this element in result. So we put current element back in list and search for place of New element in rest of list.
So much text, but is it now easier to understand what code says? Maybe or maybe not?
there
?- insert([5, 6, 30, 60, 90], 40, L).
L = [5, 6, 30, 40, 60, 90].
?- insert([5, 6, 30, 60, 90], 6, L).
L = [5, 6, 30, 60, 90].
?- insert([5, 6, 30, 60, 90], 100, L).
L = [5, 6, 30, 60, 90, 100].
?- insert([5, 6, 30, 60, 90], 0, L).
L = [0, 5, 6, 30, 60, 90].
but there are more interesting things to do with this solution because it uses a predicate like zcompare/3 which looks a bit like compare/3 but it knows integer constraints so it is possible to query:
What integers can be inserted in list [1,3,4]?
?- insert([1,3,4], X, R).
R = [X, 1, 3, 4],
X in inf..0 ;
X = 1,
R = [1, 3, 4] ;
X = 2,
R = [1, 2, 3, 4] ;
X = 3,
R = [1, 3, 4] ;
X = 4,
R = [1, 3, 4] ;
R = [1, 3, 4, X],
X in 5..sup.
So you can insert any integer < 1 at front, or you can "insert" 1 that was there, or you can insert 2 between 1 and 3, or you can "insert" 3 or 4, or you can insert 5 or anything larger at the end of list.
Another way :
% First element of the list is smaller than V
% we keep on wth the rest of the list
insert([H | T], V, [H|V1]) :-
H < V, !, % remove choice points
insert(T, V, V1).
% First element of the list is equal than V
% insert([V | T] , V, [V|T]).
% corrected after **enoy** remark
insert([V | T] , V, [V|T]):- !.
% First element of the list is greater than V, found the place of V
insert([H | T] , V, [V,H|T]).
% insert V in an empty list (V is greater than all elements of the list)
insert([], V, [V]).
with the same results as the Users9213 answer.
EDIT A way to avoid cut is
% First element of the list is smaller than V
% we keep on with the rest of the list
insert([H | T], V, [H|V1]) :-
H < V,
insert(T, V, V1).
% First element of the list is equal than V
insert([V | T] , V, [V|T]).
% First element of the list is greater than V, found the place of V
insert([H | T] , V, [V,H|T]):-
H > V.
% insert V in an empty list (V is greater than all elements of the list)
insert([], V, [V]).

Prolog : Iterating over a list and creating a predicate

I'm creating a predicate enum that takes a list and a number for example [1,2,3,4] and 3 and returns a list that contains lists of length 3 made out of the list introduced. So in the example given enum([1,2,3,4],3,[[1,2,3],[2,3,4]]).
I've created a function take that takes only the first list of length N but I get errors when I try to loop it to get all of the others. Thanks you for helping.
append([],L,L).
append([H|T],L2,[H|L3]):- append(T,L2,L3).
len([],0).
len([_|B],X):- len(B,X1), X is X1+1.
take(_,X,Y) :- X =< 0, !, X =:= 0, Y = [].
take([],_,[]).
take([A|B],X,[A|C]):- Z is X-1, take(B,Z,C).
enum([],_,[]).
enum([N1|N2],N3,N4):-
len([N1|N2],U),
N3=<U,
take([N1|N2],N3,T1),
append([N4],[T1],T2),
!,
enum(N2,N3,T2).
I will focus on the take/3 predicate, which is the core of your question. In order to get a sublist like [2,3,4] of [1,2,3,4], you have to be able to skip the first element and just take a sublist of the rest.
You can achieve this by adding this clause to your definition:
take([_|Xs], N, Ys) :- take(Xs, N, Ys).
With this you now get several different sublists of length 3, but also some other superfluous solutions:
?- take([1,2,3,4], 3, Xs).
Xs = [1, 2, 3] ;
Xs = [1, 2, 4] ;
Xs = [1, 2] ;
Xs = [1, 3, 4] ;
Xs = [1, 3] ;
Xs = [1, 4] ;
Xs = [1] % etc.
This is because your clause take([], _, []) accepts an empty list as a "sublist of any length" of an empty list. I think you only wanted to accept the empty list as a sublist of length 0. If you remove this clause, your first clause will enforce that, and you only get solutions of length exactly 3:
?- take([1,2,3,4], 3, Xs).
Xs = [1, 2, 3] ;
Xs = [1, 2, 4] ;
Xs = [1, 3, 4] ;
Xs = [2, 3, 4] ;
false.
As a side note, your first clause is fine as is, but it can be simplified a bit to:
take(_,X,Y) :- X = 0, !, Y = [].
I would also advise you to use more readable variable names. For numbers like list lengths, we often use N. For lists, it's customary to use names like Xs, Ys, etc., with X, Y, etc. for members of the corresponding list.
Finally, to find all solutions of a predicate, you need to use a system predicate like setof, bagof, or findall. There is no way to write your enum in pure Prolog.
Because I am not sure about the advice in the other answer, here is my take on your problem.
First, don't define your own append/3 and length/2, append/3 is by now Prolog folklore, you can find it in textbooks 30 years old. And length/2 is really difficult to get right on your own, use the built-in.
Now: to take the first N elements at the front of a list L, you can say:
length(Front, N),
append(Front, _, L)
You create a list of the length you need, then use append/3 to split off this the front from the list you have.
With this in mind, it would be enough to define a predicate sliding_window/3:
sliding_window(L, N, [L]) :-
length(L, N).
sliding_window(L, N, [W|Ws]) :-
W = [_|_], % W should be at least one long
length(W, N),
append(W, _, L),
L = [_|L0],
sliding_window(L0, N, Ws).
This kind of works, but it will loop after giving you all useful answers:
?- sliding_window([a,b], N, Ws).
N = 2,
Ws = [[a, b]] ;
N = 1,
Ws = [[a], [b]] ;
% loops
It loops because of the same little snippet:
length(Front, N),
append(Front, _, L)
With length/2, you keep on generating lists of increasing length; once Front is longer than L, the append/3 fails, length/2 makes an even longer list, and so on forever.
One way out of this would be to use between/3 to constrain the length of the front. If you put it in its own predicate:
front_n(L, N, F) :-
length(L, Max),
between(1, Max, N),
length(F, N),
append(F, _, L).
With this:
sliding_window(L, N, [L]) :-
length(L, N).
sliding_window(L, N, [W|Ws]) :-
front_n(L, N, W),
L = [_|L0],
sliding_window(L0, N, Ws).
And now it finally works:
?- sliding_window([a,b,c,d], 3, Ws).
Ws = [[a, b, c], [b, c, d]] ;
false.
?- sliding_window([a,b,c], N, Ws).
N = 3,
Ws = [[a, b, c]] ;
N = 1,
Ws = [[a], [b], [c]] ;
N = 2,
Ws = [[a, b], [b, c]] ;
false.
Exercise: get rid of the harmless, but unnecessary choice point.

Resources