1
votes

I'm trying to write prolog program that sums items from two lists and present the result in another list.

For example:

List1:

[1, 3, 4, 2]

List2:

[5, 1, 3, 0]

Result:

[6, 4, 7, 2]

So far, I have this:

list_sum([],[],[]).
list_sum([H1|T1],[H2|T2],L3):-list_sum(T1,T2,[X|L3]), X is H1+H2.

?-list_sum([1,2,3,4],[1,2,3,4],R),write(R).
6

6 Answers

3
votes

If you use SWI-Prolog you can use maplist, and module lambda found there : http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl :

:- use_module(library(lambda)).

list_sum(L1, L2, L3) :-
    maplist(\X^Y^Z^(Z is X + Y), L1, L2, L3).
2
votes

What @gusbro said. Further, you need to rearrange the order of operations and add a couple of additional special cases to deal with lists of differing lengths:

list_sum( []     , []     , []     ) .
list_sum( []     , [Y|Ys] , [Z|Zs] ) :- Z is 0+Y , list_sum( [] , Ys , Zs ) .
list_sum( [X|Xs] , []     , [Z|Zs] ) :- Z is X+0 , list_sum( Xs , [] , Zs ) .
list_sum( [X|Xs] , [Y|Ys] , [Z|Zs] ) :- Z is X+Y , list_sum( Xs , Ys , Zs ) .

You need to move the evaluation (Z is X+Y) in my example above, so that Z is evaluated before the recursion. This accomplishes two things:

  • First, it makes the predicate tail-recursive, meaning the solution is iterative and therefore doesn't consume stack space. In your code, the evaluations aren't performed until after the entire recursion is done. Each intermediate sum is kept on the stack and is evaluated right-to-left on your way back up. This means you'll blow your stack on a large list.

  • Second, evaluating each result before recursing down means you fail fast. The first sum that doesn't unify with the result fails the entire operation. Your solution fails slow. Consider 10,000,000 item lists where the first item doesn't sum to the first item in the result list: you'll traverse all 10,000,000 items, then — assuming you didn't blow your stack — you start evaluating sums right-to-left. Your predicate won't fail until the very last evalution.

2
votes

it's one liner in SWI-Prolog:

list_sum(X,Y,S) :- maplist(plus, X,Y,S).

And it works also 'backward':

?- maplist(plus, [1,2,3],Y,[3,4,5]).
Y = [2, 2, 2].
1
votes

You are almost there. Your problem is that the result of the sum should be put in the head of the second clause, and not in the recursive call!

list_sum([H1|T1],[H2|T2],[X|L3]):-list_sum(T1,T2,L3), X is H1+H2.

Note that the way you had written it, L3 which is "returned" in as a result is a list in which you have removed the head (X) from the recusive call; whereas you meant the opposite: to add an element (X) to the resulting list.

0
votes

the result should be a list, so you can't just say X is H1+H2 because X is not a list and you are only matching head of the lists with a single variable. also list_sum([],[],0) is not correct for same reason. the answer looks like this:

sum([],[],[]).
sum([H1| T1], [H2| T2], [ResH| ResT]) :- 
        sum(T1, T2, ResT),
        ResH is H1+H2.

but when you run your own code, first X is matched with H1+H2, on the second recursive call X has a value and can not be matched with head of T1+T2. so it outputs a no.

0
votes
domains
list=integer*
predicates
add(list,list,list)
clauses
add([],[],[]).
add([V1X|X],[V1Y|Y],[V1Z|Z]):-add(X,Y,Z),V1Z=V1X+V1Y.