Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subtracting or adding lists of lists in prolog?

Tags:

list

prolog

I am fairly new to prolog and am trying to mess around with lists of lists. I am curious on how to add two lists of lists or subtract them resulting in one list of list. If I have two lists of lists lets say,

SomeList = [[1,2,3,4],[5,6,7,8]]  
SomeList2 = [[1,2,3,4],[5,6,7,8]]

How could I add or subtract SomeList and SomeList2 to create a list of lists? Resulting in a sum of say

sumList([[2,4,6,8],[10,12,14,16]]) 

or vice-versa for subtraction? Any help would be appreciated not looking for code but for insight !

like image 827
bgibers Avatar asked Jan 05 '23 18:01

bgibers


2 Answers

The easiest approach is with maplist:

add(X, Y, Z) :- Z is X + Y.

op_lists(L1, L2, R) :-
    maplist(maplist(add), L1, L2, R).

Which gives:

| ?- op_lists([[1,2,3,4],[5,6,7,8]], [[1,2,3,4],[5,6,7,8]], R).

R = [[2,4,6,8],[10,12,14,16]]

yes
| ?-

In the expression:

maplist(maplist(add), L1, L2, R).

maplist(G, L1, L2, R) calls G on each element of L1 and L2, resulting in each element of R. Since each element of L1 and L2 is a list, then G in this case is maplist(add) which calls add on each element of the sublists.

You can obviously modify add(X, Y, Z) to be whatever operation you wish on each pair of elements. You can also make the addition more "relational" by using CLP(FD):

add(X, Y, Z) :- Z #= X + Y.

Then you also get, for example:

| ?- op_lists([[1,2,3,4],[5,6,7,8]], L, [[3,6,9,12],[10,12,14,16]]).

L = [[2,4,6,8],[5,6,7,8]]

yes
| ?-

If you wanted to do this without maplist, you could still use add/3 and use a two-layer approach:

op_lists([], [], []).
op_lists([LX|LXs], [LY|LYs], [LR|LRs]) :-
    op_elements(LX, LY, LR),
    op_lists(LXs, LYs, LRs).

op_elements([], [], []).
op_elements([X|Xs], [Y|Ys], [R|Rs]) :-
    add(X, Y, R),
    op_elements(Xs, Ys, Rs).

You can see the simple list processing pattern here, which the use of maplist takes care of for you.

like image 120
lurker Avatar answered Jan 14 '23 09:01

lurker


Besides the solutions presented by @lurker (+1), I would also add the possibility to use DCGs, since you are working on lists. For the available operations I suggest to define a slightly more general predicate opfd/4 instead of add/3. Here are exemplary rules for addition and subtraction as asked in your question, you can use these as templates to add other two-place arithmetic operations:

opfd(+,X,Y,Z) :-
   Z #= X+Y.
opfd(-,X,Y,Z) :-
   Z #= X-Y.

As the desired operation is an argument, you only need one DCG-rule to cover all operations (marked as (1) at the corresponding goal). This way, of course, you have to specify the desired operation as an argument in your relation and pass it on to the DCGs. The structure of these DCGs is very similar to the last solution presented by @lurker, except that the resulting list does not appear as an argument since that is what the DCGs describe. For easier comparison I will stick with the names op_lists//3 and op_elements//3, the calling predicate shall be called lists_op_results/4:

lists_op_results(L1,L2,Op,Rs) :-
   phrase(op_lists(Op,L1,L2),Rs).

op_lists(_Op,[],[]) -->
   [].
op_lists(Op,[X|Xs],[Y|Ys]) -->
   {phrase(op_elements(Op,X,Y),Rs)},
   [Rs],
   op_lists(Op,Xs,Ys).

op_elements(_Op,[],[]) -->
   [].
op_elements(Op,[X|Xs],[Y|Ys]) -->
   {opfd(Op,X,Y,R)},                    % <-(1)
   [R],
   op_elements(Op,Xs,Ys).

Example queries:

   ?- lists_op_results([[1,2,3,4],[5,6,7,8]], [[1,2,3,4],[5,6,7,8]], +, R).
R = [[2,4,6,8],[10,12,14,16]]
   ?- lists_op_results([[1,2,3,4],[5,6,7,8]], [[1,2,3,4],[5,6,7,8]], -, R).
R = [[0,0,0,0],[0,0,0,0]]

@lurker's example:

   ?- lists_op_results([[1,2,3,4],[5,6,7,8]], L, +, [[3,6,9,12],[10,12,14,16]]).
L = [[2,4,6,8],[5,6,7,8]]

You can also ask if there is an operation that fits the given lists:

   ?- lists_op_results([[1,2,3,4],[5,6,7,8]], L, Op, [[3,6,9,12],[10,12,14,16]]).
L = [[2,4,6,8],[5,6,7,8]],
Op = + ? ;
L = [[-2,-4,-6,-8],[-5,-6,-7,-8]],
Op = -

On a sidenote: Since the operation is the first argument of opfd/4 you can also use it with maplist as suggested in @lurker's first solution. You just have to pass it lacking the last three arguments:

   ?- maplist(maplist(opfd(Op)),[[1,2,3,4],[5,6,7,8]], L, [[3,6,9,12],[10,12,14,16]]).
L = [[2,4,6,8],[5,6,7,8]],
Op = + ? ;
L = [[-2,-4,-6,-8],[-5,-6,-7,-8]],
Op = -
like image 25
tas Avatar answered Jan 14 '23 11:01

tas