Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - finding all combinations (The product) of List of Lists (of Lists)

I'v tried a few features to implement a predicate which finds all the combinations, like in that example:

List = [[1, 2], [1, 2, 3]]

These should be the output,

Comb = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]] 

but all the solutions I've found were using findall which I don't want to use in my task.

How can I differently implement the predicate, avoiding findall ?

Or maybe how can I implement my_findall without using any built-in features?

A solution like here, without built-in predicates would be great

Thanks to the helpers!

like image 728
Ilya.K. Avatar asked Jun 23 '17 23:06

Ilya.K.


1 Answers

I'm not so sure this is the most efficient approach, but it's fairly transparent. The idea here is to define the problem in recursive (or inductive) "layers":

% multiply_lists(ListOfLists, MultipliedListOfLists)
%
% The first two clauses handle the case where ListOfLists consists
%   of just one list
% The third clause handles the general case
%
multiply_lists([[X]], [[X]]).
multiply_lists([[X|Xs]], [[X]|T]) :- 
    multiply_lists([Xs], T).
multiply_lists([E|Es], R) :-
    multiply_lists(Es, R1),
    multiply_list(E, R1, R).

% multiply_list relates the product of a list of lists and a single list
%    of elements
%
multiply_list([], _, []).
multiply_list([E|Es], L, Ls) :-
    multiply_list(Es, L, LL),
    multiply_element(E, L, LL, Ls).

% multiply_element relates the product, prepended to a given list,
%   of a single list of lists and a single element 
%
multiply_element(_, [], A, A).
multiply_element(X, [Y|Ys], A, [[X|Y]|T]) :-
    multiply_element(X, Ys, A, T).

multiply_element/4 actually combines two rules into one: it defines multiplication of a list by a single element, and prepends those results as individual elements to a given list.

A sample result:

| ?- multiply_lists([[1, 2], [1, 2, 3]], L).

L = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]] ? ;

no
| ?- multiply_lists([[a,b,c], [1,2], [x,y]], L).

L = [[a,1,x],[a,1,y],[a,2,x],[a,2,y],[b,1,x],[b,1,y],[b,2,x],[b,2,y],[c,1,x],[c,1,y],[c,2,x],[c,2,y]] ? ;

no

Some quirks with the above implementation:

  • It's not tail recursive (so will use more stack as the lists get longer)
  • It leaves a choice point

But it does illustrate how the problem can be solved without using append/3 or other list-based pre-defined predicates.

like image 163
lurker Avatar answered Oct 17 '22 05:10

lurker