0

Most probably I couldn't properly phrase my search keywords. What I'm trying to do is to recursively evaluate difference of consecutive entries in a list and keep adding them to a global counter. The challenge is the circular clause in the end so that if the list is [5,4,3,2,1], the predicate should return (5-4)+(4-3)+(3-2)+(2-1)+(1-5)=0.

What I have so far is as follows:

circularSumOfDifferences([_],0).
circularSumOfDifferences([E1,E2|List],Sum) :-
    Diff is E1-E2,
    circularSumOfDifferences([E2|List],SoD),
    Sum is Diff+SoD.

Now I have thought of defining another predicate to add the first element of the list at the tail of given list and calling the new list in conjunction with the query: copyHeadToTail([5,4,3,2,1],Result),circularSumOfDifferences(Result,C).

It won't be difficult to realise but it doesn't look very elegant to me. I wonder if there's a way to define just one predicate circularSumOfDifferences(+L,-SoD) that recurses through the list and runs one statement in the end (or beginning) for that one substraction,i.e. 1-5.

false
  • 10,264
  • 13
  • 101
  • 209
Donbhupi
  • 222
  • 5
  • 18

1 Answers1

3

You can write a predicate that does that, of course, but you should have noticed that every element X of the list appears twice in the total sum, once as X and once as -X. No matter what list of numbers you give this predicate, you will always have 0 as a result. Is that a homework? It is very probable that the point was to notice this instead of trying to calculate the actual sum:

always_zero([], 0).
always_zero([X|Xs], 0) :-
    number(X),
    always_zero(Xs, 0).

Here is how you might want to write the predicate, if you insist:

circ_sum_of_diff([X|Xs], Sum) :- % must have at least one element I guess?
    circ_sum_of_diff_1(Xs, X, X, 0, Sum).

circ_sum_of_diff_1([], Last, First, SoFar, Sum) :-
    Sum is SoFar + Last - First.
circ_sum_of_diff_1([X|Xs], X0, First, SoFar0, Sum) :-
    SoFar is SoFar0 + X0 - X,
    circ_sum_of_diff_1(Xs, X, First, SoFar, Sum).

This uses an auxiliary predicate, which is quite common for Prolog programs. The auxiliary predicate that actually iterates over the list has three additional arguments: the unpacked head of the list, the original first element (used at the end of the list), and the sum so far.

  • thanks for introducing me to auxiliary predicates, this is exactly what I wanted. I may have seen it earlier but never included it in my toolbox. Btw I rephrased my question out of a TSP problem where the list will contain the towns to visit and so the salesman has to reach the first town after the last town. I usually phrase such customised questions to return 0 on success. – Donbhupi Feb 07 '16 at 21:10
  • @Donbhupi I don't fully understand your comment. It doesn't make sense to "return" anything on success. Also, if you need to make a term like `[a-b, b-c, ...]` you definitely don't need any arithmetic nor numbers, and this maybe invalidates my answer. –  Feb 08 '16 at 08:43
  • read the `return` as `evaluates as 0` considering it's Prolog. The actual predicate takes in a list of towns `[A,B,C,D]` and calls another predicate to calculate the Hamiltonian distance. I just rephrased that question as simple useless arithmetic to understand how it's done in Prolog. – Donbhupi Feb 09 '16 at 18:41