Arithmetic
As you have already discovered, arithmetic can be processed in Prolog using the (is)/2 operator. This is because in Prolog all this is just a symbolic calculus: all things have no default value, so unification (=)/2 will not know that (+)/2 refers to the addition, for example.
Now your problem is that you are using a regular predicate inside (is)/2 (here it is your recursive call). Since (is)/2 only performs arithmetic, it does not evaluate the predicate call. He does not even recognize it, since it is not an arithmetic function.
The fix here should affect the result of recursively calling the variable, and then use it in the (is)/2 call:
listsum(X,[]). listsum(Result, [Head|Tail]) :- listsum(SumOfTail, Tail), Result is Head + SumOfTail.
Base Register Validity
But if you check this code, you will not get the desired result. The reason is that you have another problem in your base case. The sum of the empty list is not โnothing,โ as you stated by writing
listsum(X,[]).
( X is a free variable, therefore, anything can be).
Instead, it is 0 :
listsum(0, []).
Final code:
listsum(0, []). listsum(Result, [Head|Tail]) :- listsum(SumOfTail, Tail), Result is Head + SumOfTail.
Argument Order
Now, as a passage in Prolog, the convention is that the output variables should be placed at the end of the predicate, while the input variables should be placed at the beginning of the predicate, therefore, to behave as we would like, we could reorganize the following:
listsum([], 0). listsum([Head|Tail], Result) :- listsum(Tail, SumOfTail), Result is Head + SumOfTail.
Time-based call optimization
Now we can improve this predicate using more advanced methods. For example, we could introduce tail calls so that we can optimize Tail Call Optimization (googlable), thanks to the declarative programming idiom called battery:
listsum(List, Sum) :- listsum(List, 0, Sum). listsum([], Accumulator, Accumulator). listsum([Head|Tail], Accumulator, Result) :- NewAccumulator is Accumulator + Head, listsum(Tail, NewAccumulator, Result).
The idea is to update the intermediate result at each stage of the recursion (adding the value of the current chapter of the list to it), and then simply indicate that when the list is empty, this intermediate value is the final value.
Getting More General Programs
As you may have noticed in Prolog, quite often predicates can be used in several ways. For example, length/2 can be used to determine the length of a list:
?- length([1, 2, 3], Length). Length = 3.
or create a list of skeletons with free variables of the required length:
?- length(List, 3). List = [_G519, _G522, _G525].
Here, however, you may have noticed that you cannot ask Prolog which lists have a sum of 6 :
?- listsum(L, 6). ERROR: is/2: Arguments are not sufficiently instantiated
This is because in order to โgo backโ, Prolog would have to solve the equation when the (is)/2 operator call comes in. And although your simple (only additions), arithmetic is not solvable this way in the general case.
To overcome this problem, you can use programming constraints. A very good library is available for SWI. Clpfd.
The syntax is here:
:- use_module(library(clpfd)). listsum(List, Sum) :- listsum(List, 0, Sum). listsum([], Accumulator, Accumulator). listsum([Head|Tail], Accumulator, Result) :- NewAccumulator
Now we can use our predicate in another way that we would like to use it:
?- listsum(L, 6). L = [6] ; L = [_G1598, _G1601], _G1598+_G1601
We could even ask all solutions to the problem:
?- listsum(L, X). L = [], X = 0 ; L = [X], X in inf..sup ; L = [_G2649, _G2652], _G2649+_G2652
I just mentioned this, so you understand that you should often avoid using (is)/2 , and using programming restrictions should be preferred to get the most common programs.