Reputation: 49
I need to find the combinations in a list of lists. For example, give the following list,
List = [[1, 2], [1, 2, 3]]
These should be the output,
Comb = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]]
Another example:
List = [[1,2],[1,2],[1,2,3]]
Comb = [[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3]....etc]
I know how to do it for a list with two sublists but it needs to work for any number of sublists.
I'm new to Prolog, please help.
Upvotes: 4
Views: 813
Reputation: 71099
A twist in @false's approach:
%list_comb( ++LL, -Ess)
list_comb( LL, Ess):-
is_list( LL),
maplist( is_list, LL),
findall( Es, maplist( member, Es, LL), Ess).
Testing:
41 ?- list_comb( [[1,2],[1],[1]], X). X = [[1, 1, 1], [2, 1, 1]]. 42 ?- list_comb( [[1,2],[1],[1,2,3]], X). X = [[1, 1, 1], [1, 1, 2], [1, 1, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3]]. 43 ?- list_comb( [[1,2],[],[1,2,3]], X). X = []. 44 ?- list_comb( [[1,2],t,[1,2,3]], X). false. 45 ?- list_comb( t, X). false.
Upvotes: 0
Reputation: 18726
This answer hunts the bounty offered "for a pure solution that also takes into account for Ess
".
Here we generalize this previous
answer like so:
list_crossproduct(Xs, []) :-
member([], Xs).
list_crossproduct(Xs, Ess) :-
Ess = [E0|_],
same_length(E0, Xs),
maplist(maybelonger_than(Ess), Xs),
list_comb(Xs, Ess).
maybelonger_than(Xs, Ys) :-
maybeshorter_than(Ys, Xs).
maybeshorter_than([], _).
maybeshorter_than([_|Xs], [_|Ys]) :-
maybeshorter_than(Xs, Ys).
list_crossproduct/2
gets bidirectional by relating Xs
and Ess
early.
?- list_comb(Xs, [[1,2,3],[1,2,4],[1,2,5]]). nontermination % BAD! ?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5]]). Xs = [[1],[2],[3,4,5]] % this now works, too ; false.
Sample query having multiple answers:
?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5],X,Y,Z]).
X = [1,2,_A],
Y = [1,2,_B],
Z = [1,2,_C], Xs = [[1],[2],[3,4,5,_A,_B,_C]]
; X = [1,_A,3],
Y = [1,_A,4],
Z = [1,_A,5], Xs = [[1],[2,_A],[3,4,5]]
; X = [_A,2,3],
Y = [_A,2,4],
Z = [_A,2,5], Xs = [[1,_A],[2],[3,4,5]]
; false.
Upvotes: 5
Reputation: 18726
Here's a way to do it using maplist/3
and append/2
:
list_comb([], [[]]). list_comb([Xs|Xss], Ess) :- Xs = [_|_], list_comb(Xss, Ess0), maplist(aux_x_comb(Ess0), Xs, Esss1), append(Esss1, Ess). aux_x_comb(Ess0, X, Ess1) :- maplist(head_tail_list(X), Ess0, Ess1). head_tail_list(X, Xs, [X|Xs]).
Sample query:
?- list_comb([[a,b],[f,g],[x,y,z]], Ess).
Ess = [[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z],
[b,f,x],[b,f,y],[b,f,z],
[b,g,x],[b,g,y],[b,g,z]].
Here's how it works! As an example, consider these goals:
list_comb([[a,b],[f,g],[x,y,z]], Ess)
list_comb([ [f,g],[x,y,z]], Ess0)
How can we get from Ess0
to Ess
?
We look at the answers to the latter query:
?- list_comb([[f,g],[x,y,z]], Ess0). Ess0 = [[f,x],[f,y],[f,z], [g,x],[g,y],[g,z]].
... place a
before [f,x]
, ..., [g,z]
...
?- maplist(head_tail_list(a), [[f,x],[f,y],[f,z], [g,x],[g,y],[g,z]], X). X = [[a,f,x],[a,f,y],[a,f,z], [a,g,x],[a,g,y],[a,g,z]].
... then do the same for b
.
maplist(aux_x_comb)
helps us handle all items:
?- maplist(aux_x_comb([[f,x],[f,y],[f,z], [g,x],[g,y],[g,z]]), [a,b], X). X = [[[a,f,x],[a,f,y],[a,f,z], [a,g,x],[a,g,y],[a,g,z]], [[b,f,x],[b,f,y],[b,f,z], [b,g,x],[b,g,y],[b,g,z]]].
To get from a list of lists to a list use append/2
.
I hope this explanation was more eludicating than confusing:)
Upvotes: 2
Reputation: 10122
For completeness, here is the augmented version of my comment-version. Note nilmemberd_t/2
which is inspired by memberd_t/2
.
nilmemberd_t([], false).
nilmemberd_t([X|Xs], T) :-
if_(nil_t(X), T = true, nilmemberd_t(Xs, T)).
nil_t([], true).
nil_t([_|_], false).
list_comb(List, []) :-
nilmemberd_t(List, true).
list_comb(List, Ess) :-
bagof(Es, maplist(member,Es,List), Ess).
Above version shows that "only" the first clause was missing in my comment response. Maybe even shorter with:
nilmemberd([[]|_]).
nilmemberd([[_|_]|Nils]) :-
nilmemberd(Nils).
This should work for Prologs without constraints. With constraints, bagof/3 would have to be reconsidered since copying constraints is an ill-defined terrain.
Upvotes: 2