Subtracting or adding lists of lists in prolog?

点点圈 提交于 2021-02-07 18:36:20

问题


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 !


回答1:


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.




回答2:


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 = -


来源:https://stackoverflow.com/questions/38252848/subtracting-or-adding-lists-of-lists-in-prolog

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!