Working with list of lists in Prolog - list

Please help me to solve this problem:
I have a list of lists
[[1,2],[3,4]]
How do I get:
[1,3]
[1,4]
[2,3]
[2,4]
Or if I have a list of lists
[[1,2],[3,4],[6,7]]
How do I get:
[1,3,6]
[1,3,7]
[1,4,6]
[1,4,7]
[2,3,6]
[2,3,7]
[2,4,6]
[2,4,7]

The predicate for accessing a single list element is the most basic Prolog building block: member/2.
And you want a list of all lists' elements: maplist/3 does such mapping. Thus we can write
combine(Ls, Rs) :-
maplist(get1, Ls, Rs).
get1(L, E) :-
member(E, L).
note that get1/2 is only required so that we swap the member/2 arguments. But because in (pure) Prolog we are describing relations between arguments, we can swap arguments' order and simplify it even more:
combine(Ls, Rs) :-
maplist(member, Rs, Ls).
Test output:
?- combine( [[1,2],[a,b]], Xs).
Xs = [1, a] ;
Xs = [1, b] ;
Xs = [2, a] ;
Xs = [2, b].
%% this is the same as:
%% maplist( member, Xs, [[1,2],[a,b]]) :-
%% member( X1, [1,2] ),
%% member( X2, [a,b]), Xs = [X1,X2].
edit
A joke: really, my first combine/2 should have been written like
combine(Ls, Rs) :-
maplist(rebmem, Ls, Rs).
rebmem(L, E) :-
member(E, L).

You can do something like this:
lists([], []).
lists([[Head|_]|Lists], [Head|L]):-
lists(Lists, L).
lists([[_,Head|Tail]|Lists], L):-
lists([[Head|Tail]|Lists], L).
That is, take the first element of the first list in your input list and continue recursively with the remaining lists. As a second chance, skip that element and redo with the remaining elements.

Related

SWI Prolog - Combining elements from two lists into pairs

How can I "join" two lists while keeping their elements's positions? Like, I have List1 = [1,2,3] and List2 = [a,b,c]. How can I get the list [[1,a], [2,b], [3,c]]?
I've been trying to look for built-in predicates but all I've found can't reproduce this.
join_2_lists(L1, L2, JoinLst) :-
must_be(list, L1),
must_be(list, L2),
join_2_lists_(L1, L2, JoinLst).
join_2_lists_([], [], []).
join_2_lists_([H1|T1], [H2|T2], [[H1, H2]|JoinLst]) :-
join_2_lists_(T1, T2, JoinLst).
Result in swi-prolog:
?- join_2_lists([1, 2, 3], [a, b, c], J).
J = [[1,a],[2,b],[3,c]].

Subset sum Prolog

Define a predicate subsetsum(L, Sum, Subl) that takes a list L of numbers, a number Sum, and unifies SubL with a sub sequence of L such that the sum of the numbers in SubL is Sum.
For example
?- subsetsum([1,2,5,3,2], 5, SubSet);
SubSet = [1,2,2];
SubSet = [2,3];
SubSet = [5];
SubSet = [3,2];
No.
we have
sum([H1 | [H2 | Tail]], S):-
sum([[H1+H2]|Tail], S):-
sum([X], X).
and
subset([],[]).
subset([H1|T1], [H1|T2]) :- // heads are the same
subset(T1, T2).
subset([_|Rest], X):
subset(Rest, X).
If all numbers used are integers and your Prolog processor supports clpfd, proceed like this!
:- use_module(library(clpfd)).
z_z_product(A,B,AB) :-
AB #= A*B.
subsetsum_(Zs, Sum, Bs, [Sum|Vs]) :-
same_length(Zs, Bs),
append(Zs, Bs, Vs),
Bs ins 0..1,
maplist(z_z_product, Zs, Bs, Xs),
sum(Xs, #=, Sum).
Sample query:
?- subsetsum_([1,2,5,3,2], 5, Sel, Vs), labeling([], Vs).
Sel = [0,0,0,1,1], Vs = [5,1,2,5,3,2,0,0,0,1,1]
; Sel = [0,0,1,0,0], Vs = [5,1,2,5,3,2,0,0,1,0,0]
; Sel = [0,1,0,1,0], Vs = [5,1,2,5,3,2,0,1,0,1,0]
; Sel = [1,1,0,0,1], Vs = [5,1,2,5,3,2,1,1,0,0,1]
; false.
The following clauses should do what you need...
subsetsum(SET, SUM, ANSWER) :-
% Find a subset
subset(SET, ANSWER),
% Check elements of the subset add up to SUM
sum(ANSWER, SUM).
% sum(LIST, SUM) - sums all numbers in the list
sum([], 0).
sum([X | T], SUM) :-
sum(T, TAILSUM),
SUM is TAILSUM + X.
% subset - finds subsets
subset([], []).
subset([E|Tail], [E|NTail]) :-
subset(Tail, NTail).
subset([_|Tail], NTail) :-
subset(Tail, NTail).
With swi-prolog we can use the library predicate sum_list/2 together with that subset/2 you already got! Note that I gave subset/2 the better fitting name list_subsequence/2:
list_subsequence([], []).
list_subsequence([X|Xs], [X|Ys]) :-
list_subsequence(Xs, Ys).
list_subsequence([_|Xs], Ys) :-
list_subsequence(Xs, Ys).
subsetsum(List, Sum, Sub) :-
list_subsequence(List, Sub),
sum_list(Sub, Sum).
Here is the sample query that you gave:
?- subsetsum([1,2,5,3,2], 5, Xs).
Xs = [1,2,2]
; Xs = [2,3]
; Xs = [5]
; Xs = [3,2]
; false.
OK! Let's run another query with both integers and floats... does that work, too?
?- subsetsum([1,2.1,5,3,2], 5.1, Xs).
Xs = [1,2.1,2]
; Xs = [2.1,3]
; false.
Looks alright to me!

Intersection of two lists without duplicate elements in Prolog

I need to write a program that finds the intersection of two lists. I can't use cuts and there shouldn't be any duplicate elements in the result list.
This is my code:
intersection([],_,[]).
intersection([X|Xs],Y,[X|Zs]) :-
member(X,Y),
intersection(Xs,Y,Zs).
intersection([_|Xs],Y,Zs) :-
intersection(Xs,Y,Zs).
When I run the following query, I get these answers:
?- intersection([a,b,c,a],[a,v,c],L).
L = [a, c, a] ;
L = [a, c] ; % <---------- this is only answer I want to get
L = [a, a] ;
L = [a] ;
L = [c, a] ;
L = [c] ;
L = [a] ;
L = [].
What can I do? I want to get L = [a,c] and nothing else... Can you help?
In my answer to the related question "Intersection and union of 2 lists" I presented the logically pure predicate list_list_intersectionSet/3. It should fit your requirements to a T!
Here's is a brushed-up version of list_list_intersectionSet/3, which is based on:
monotone conditional if_/3,
meta-predicate tfilter/3,
and the reified test predicates dif/3 and memberd_t/3.
Here we go:
list_list_intersectionSet([] ,_ ,[]).
list_list_intersectionSet([A|As0],Bs,Cs0) :-
if_(memberd_t(A,Bs), Cs0 = [A|Cs], Cs0 = Cs),
tfilter(dif(A),As0,As),
list_list_intersectionSet(As,Bs,Cs).
Let's see it in action!
?- list_list_intersectionSet([a,b,c,a],[a,v,c],L).
L = [a,c].
If by "conjunction" you mean "intersection", you should take a look at the implementation in the SWI-Prolog library(lists) of the predicate intersection/3. It contains cuts, but you can leave them out if you don't mind all the choicepoints.
With it:
?- intersection([a,b,c,a],[a,v,c],I).
I = [a, c, a].
Of course, this doesn't work even in the library predicate, because you need sets with your current definition. (It is enough if only the first argument is a set.)
You can make sets with the sort/2 predicate: if the first argument is a list with repetitions, the second argument will be a sorted list without repetitions, for example:
?- sort([a,b,c,a], S1), intersection(S1, [a,v,c], I).
S1 = [a, b, c],
I = [a, c].
or maybe:
?- sort([a,b,c,a], S1), intersection(S1, [a,v,c,c,a,c], I).
S1 = [a, b, c],
I = [a, c].
?- sort([a,b,c,a,b,c,a,b,c], S1), intersection(S1, [a,v,c,c,a,c], I).
S1 = [a, b, c],
I = [a, c].
If you sort both arguments, you can use a ord_intersection/3 from library(ordsets), implemented in terms of oset_int/3.
?- sort([a,b,c,a], S1), sort([a,v,c,c,a,c], S2), ord_intersection(S1, S2, I).
S1 = [a, b, c],
S2 = [a, c, v],
I = [a, c].
Importantly, oset_int/3 does not use any cuts in its implementation. It however assumes that the first and second arguments are lists of elements sorted by the "standard order of terms" and without duplicates, as done by sort/2.
If for some reason you don't want to use sort/2, you could maybe use an accumulator and check against it before taking an element to the intersection:
my_intersection(Xs, Ys, Zs) :-
my_intersection_1(Xs, Ys, [], Zs).
my_intersection_1([], _, Zs, Zs).
my_intersection_1([X|Xs], Ys, Zs0, Zs) :-
member(X, Ys), \+ member(X, Zs0),
my_intersection_1(Xs, Ys, [X|Zs0], Zs).
my_intersection_1([_|Xs], Ys, Zs0, Zs) :-
my_intersection_1(Xs, Ys, Zs0, Zs).
Of course, the order of the elements in the result will be now reversed. If this is not what you mean by "conjunction", you could for example rewrite the first two clauses of my_intersection_1/4 as:
my_intersection_1([], _, _, []).
my_intersection_1([X|Xs], Ys, Zs0, [X|Zs]) :-
member(X, Ys), \+ member(X, Zs0),
my_intersection_1(Xs, Ys, [X|Zs0], Zs).
The previously shown list_list_intersectionSet/3 restricts the item order in the intersection:
?- list_list_intersectionSet([a,b],[a,b], [a,b]).
true.
?- list_list_intersectionSet([a,b],[a,b], [b,a]).
false.
In this answer we lift that restriction... preserving logical-purity and determinism (for ground cases)!
First, we define none_intersect/2 using Prolog lambdas and
meta-predicate maplist/2.
none_intersect(As,Bs) states that all members in As are different from all members in Bs.
:- use_module(library(lambda)).
none_intersect(As,Bs) :-
maplist(\A^maplist(dif(A),Bs),As).
Next, we define intersection_of_and/3---based on none_intersect/2 (defined above), meta-predicate tpartition/4 and reified term equality (=)/3:
intersection_of_and([],As,Bs) :-
none_intersect(As,Bs).
intersection_of_and([X|Xs],As0,Bs0) :-
tpartition(=(X),As0,[_|_],As), % [_|_] = [X|_]
tpartition(=(X),Bs0,[_|_],Bs), % [_|_] = [X|_]
intersection_of_and(Xs,As,Bs).
intersection_of_and(Xs,As,Bs) states that
all items which occur in both As and Bs also occur in Xs (first clause),
all items in Xs occur in both As and Bs at least once (second clause),
and the list Xs does not contain any duplicates.
intersection_of_and/3 uses a specific argument in order to enable first argument indexing.
Last, we define list_list_intersection/3 which has the argument order that the OP used:
list_list_intersection(As,Bs,Xs) :-
intersection_of_and(Xs,As,Bs).
Let's run some queries! First, the query that the bounty offerer suggested:
?- list_list_intersection([a,b],[a,b], [b,a]).
true.
Next, a similar query with 3 distinct items in 3 lists having 3 different orders:
?- list_list_intersection([a,b,c],[b,a,c], [c,a,b]).
true.
What if some x only occurs in the first/second list?
?- list_list_intersection([a,b,c,x],[b,a,c], [c,a,b]).
true.
?- list_list_intersection([a,b,c],[b,a,c,x], [c,a,b]).
true.
What if some item occurs twice in the first/second list?
?- list_list_intersection([a,b,c],[b,a,c,b], [c,a,b]).
true.
?- list_list_intersection([a,b,c,c],[b,a,c], [c,a,b]).
true.
Last, what if the intersection contains duplicates?
Intersections are not to contain duplicates...
?- list_list_intersection([a,b,c],[b,a,c], [c,c,a,b]).
false. % as expected
Seems like something like this would be the easy way:
intersection( Xs , Ys , Zs ) :-
sort(Xs,X1) , % order and de-dupe the 1st list so as to produce a set
sort(Ys,Y1) , % order and de-dupe the 2nd list so as to produce a set
merge(Xs,Ys,Zs) % merge the two [ordered] sets to produce the result
. % easy!
merge( [] , [] , [] ) .
merge( [] , [_|_] , [] ) .
merge( [_|_] , [] , [] ) .
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- X = Y , merge( Xs , Ys , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X < Y , merge( Xs , [Y|Ys] , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X > Y , merge( [X|Xs] , Ys , Zs ) .
Or even just this [not-terribly-performant] one-liner:
intersection( Xs , Ys , Zs ) :- setof(Z,(member(Z,Xs),member(Z,Ys)),Zs).
This can be solved by simple set theory:
intersection(A,B,AnB):-
subtract(A,B,AminusB),
subtract(A,AminusB,K),
sort(K,AnB).
For the query:
?- intersection([a,b,c,a],[a,v,c],L).
output is
L = [a, c].
No more answers.

Merge lists of lists in prolog

I would like to perform something like:
merge([[[],[],[],[t1]],[[],[],[],[t2,t3]]], X).
where X would return as: [[],[],[],[t1,t2,t3]].
But I have tried everything to my prolog knowledge and came up with nothing.
Any hints?
Imagine it as:
Computer(
Tasklist1(
core1[sometasks],core2[sometasks],...,coreX(sometasks)),
...
TasklistX(
core1[sometasks],core2[sometasks],...,coreX(sometasks))
)
so the tasklist after tasklist1 needs to be scheduled on the same cores, after the tasks of tasklist1.
It's not totally clear what the limits of the problem may be. But here is a solution which assumes you may have more than two inner list-of-lists, and the count of the innermost lists might vary.
merge2(L, [], L) :- L \= [].
merge2([], L, L).
merge2([LH1|LT1], [LH2|LT2], [LH3|LT3]) :-
append(LH1, LH2, LH3),
merge2(LT1, LT2, LT3).
merge([L], L).
merge([H1,H2|T], R) :-
merge2(H1, H2, H),
merge([H|T], R).
So,
| ?- merge([[[],[],[],[t1]],[[],[],[],[t2,t3]]], L).
L = [[],[],[],[t1,t2,t3]] ? ;
no
| ?- merge([[[1],[2],[3]], [[4],[5],[6]],[[a],[b],[c,d]]], L).
L = [[1,4,a],[2,5,b],[3,6,c,d]] ? a
no
| ?- merge([[[1],[2],[3]], [[5],[6]],[[a],[b],[c,d]]], L).
L = [[1,5,a],[2,6,b],[3,c,d]] ? ;
(1 ms) no
| ?-
If you want to restrict the innermost list counts to be the same, you can replace merge2 with maplist, and the merge predicate simply becomes:
merge([L], L).
merge([H1,H2|T], R) :-
maplist(append, H1, H2, H),
merge([H|T], R).
I thought it could be easier...
merge(L, R) :-
maplist(length_list(N), L),
findall(S, (
between(1,N,I),
findall(Zs, (
member(Z,L),
nth1(I,Z,Zs)), T),
append(T, S)), R).
length_list(Len, L) :- length(L, Len).

Adding an element to a sublist

I need to define a predicate toAdd/3 such that if Xss and Yss are lists of lists then toAdd(X,Xss,Yss) holds if Yss can be obtained by adding the element X to the end of every element in Xss, e.g.
?- toAdd(g, [[e],[b,c,f],[k,h]], Yss).
Yss = [[e,g],[b,c,f,g],[k,h,g]]. % expected result
I know how to add an element to a list but lists of lists confuse me.
I wrote this code that adds to the end of one list, but not sublists.
add(X,[],[X]).
add(X,[A|L],[A|L1]) :-
add(X,L,L1).
Let's put the predicate add/3 to use with meta-predicate maplist/3!
toAdd(X,Xss,Yss) :-
maplist(add(X),Xss,Yss).
Sample query:
?- toAdd(g, [[e],[b,c,f],[k,h]], Yss).
Yss = [[e,g],[b,c,f,g],[k,h,g]]
; false.
split the problem in two parts:
- transform each element
- append to tail
then
add(_,[],[]). % done
add(X,[E|Es],[T|Ts]) :-
append(E,[X],T),
add(X,Es,Ts).
we can do inline using findall/3 and member/2
1 ?- [user].
|: tooAdd(X, Es, Ts) :- findall(T, (member(E,Es),append(E,[X],T)), Ts).
% user://1 compiled 71.19 sec, 2 clauses
true.
2 ?- tooAdd(g, [[e], [b, c, f], [k, h]], Yss).
Yss = [[e, g], [b, c, f, g], [k, h, g]].