How to get the sum of given numbers in the prologue?

I am new to the prologue and I am doing some exercises for practice. Therefore, I am trying to get the sum of the data in the list. I am trying to use this:

my_last(X, [X]). my_last(X, [_|L]) :- my_last(X, L). 

(from here )

like my guidebook. So this is my code to get the amount:

 listsum(X, []). listsum(X, [H|L]):- X is H + listsum(X, L). 

when I compile it, he says

practice.pl: evaluation listsum(_G139,_G140) does not exist
practice.pl:2: Singleton variables: [X]

then when trying listsum(0, [1,2,3]). it returns false .

I still do not really understand prolog, list and recursion in prolog.

+8
list sum prolog clpfd
source share
2 answers

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 #= Accumulator + Head, listsum(Tail, NewAccumulator, Result). 

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#=6 ; L = [_G1712, _G1715, _G1718], _G1712+_G1715#=_G1728, _G1728+_G1718#=6 . % Here I interrupted the answer but it would not terminate. 

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#=X . % Here I interrupted the answer but it would not terminate 

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.

+24
source share

If possible, use clpfd of plain old (is)/2 (and friends).

clpfd offers a logically clean predicate sum/3 that can suit your needs!

+2
source share

All Articles