3

Hoping you are well. I am fairly new to prolog and I am having an issue with a code that I am writing. The purpose of the code is quite simple. It adds each element from a list until the last one. Something I could do in Java as:

    static void add(double[] array){
        double x = .0;
        for (int i = 0; i < array.length; ++i)
            x += array[i];
        System.out.println(x);
    }

However, I have been scratching my head about how to do it in prolog. I have the following code

add_list([], X):- write(X).
add_list([Head|Tail],X) :- 
    Y is Head,
    X is 0 + Y, %initialize X and add Y every time it runs.
    add_list(Tail, X).

The error I do get is that the variable X is already bounded when the code runs for the second time which makes sense, but I don't really know how to go about solving the issue.

Any help will be greatly appreciated.

Thanks.

false
  • 10,264
  • 13
  • 101
  • 209
Alm4my
  • 33
  • 3
  • `X` appears in three places in the second clause - you are using it the same as a variable in languages that change their state using assignment. Be aware that in this setting this means that Prolog will _make sure that_ or _ascertain that_ the same values appears in those three places. `X` is best though of as a reference to a shared memory location. Evidently `add_list([Head|Tail],X)` cannot have the same `X` as `add_list(Tail,X)` on its second argument location. – David Tonhofer Feb 21 '20 at 14:07
  • I understand. Thank you. – Alm4my Feb 22 '20 at 16:12

2 Answers2

5

Prolog programs are relations. Using a name starting with add_ sounds more like an imperative program. If you want to learn Prolog, avoid such names as much as you can. In this case you want to establish a relation between a list and the sum of its elements. list_sum/2 sounds like a much better name.

:- use_module(library(clpz)). % or clpfd
:- op(150, fx, #).

list_sum([], 0).
list_sum([E|Es], S) :-
   #S #= #E + #Si,
   list_sum(Es, Si).

Now, given that definition, try it out!

?- list_sum([1,2,3], N).
   N = 6.
?- list_sum([1,2,3], 7).
   false.
?- list_sum([1,2,E], 7).
   E = 4.
?- list_sum([1,E,E], 7).
   E = 3.
false
  • 10,264
  • 13
  • 101
  • 209
  • This uses [constraint programming](https://github.com/triska/clpz) and the `#=` operator instead of _"function evaluation on the right-hand-side, and [unification](https://en.wikipedia.org/wiki/Unification_(computer_science)) with the left-hand-side as in `LHS is RHS`_, but why declare a `#` infix operator to prefix `S` to `#S` for example? – David Tonhofer Feb 21 '20 at 14:01
  • Otherwise `N = 1+0, list_sum([1], N).` succeeds whereas `list_sum([1], N), N = 1+0.` fails. Thus, non-relational. – false Feb 21 '20 at 14:41
  • I see. But for the first goal, one might have reason to see it succeed, whereas `list_sum([1], N), N = 1+0.` will never succeed because the programmer messed up bad, trying to unify an integer and a term. In fact, a compiler might succeed in telling the programmer that `list_sum([1], N), N = 1+0.` will always yield false, no matter what. So is it reasonable to preclude success for `N = 1+0, list_sum([1], N).`? – David Tonhofer Feb 21 '20 at 19:40
  • It is virtually impossible to catch **all** such cases by a compiler. Also, what about `N = 0+0` which never works for `[]` – false Feb 21 '20 at 19:42
  • Of course, it is provably impossible to catch everything. But one does not need perfection, for example the Idris compiler will tell you when it suspects your loop isn't going to terminate, which can be useful. Anyway, `N = 0+0` will indeed not work, unless you have first reduction of `0+0`. I guess that's for another language (unification with equality, innit?) – David Tonhofer Feb 21 '20 at 21:14
  • `:- use_module(library(clpfd)). :- op(150, fx, #).` are those library that you are using? I am not familiar with those. – Alm4my Feb 22 '20 at 17:45
  • Also do you have any documentation on prolog you can recommend? Those that I have seen so far are quite hard to understand. – Alm4my Feb 22 '20 at 17:46
  • I have both Linux & Windows. – Alm4my Feb 22 '20 at 18:07
  • gprolog would be it I suppose. – Alm4my Feb 22 '20 at 18:31
  • On GNU-Prolog remove the 3 prefix #. Otherwise it should work – false Feb 22 '20 at 18:33
  • @false I wish. But I have served far too long in "industry" in the desert of Java the Hutt and his merry frameworks and reinvented square wheels. Declarative Languages have an actual theory that goes beyond "changing state arbitrarily at every step", this piques my interest. – David Tonhofer Mar 01 '20 at 10:43
  • @David: OTOH, being over-ambitious can easily lead to overlook the small and still critical issues. Think of the [type testing deaster](https://stackoverflow.com/q/27306453/772868), which was caused by the omission of errors and their incorrect replacement by silent failure in DEC10. – false Mar 01 '20 at 11:11
0

I came up with a way to get the result I need. Following the advice of @false I renamed my rule from add_list to list_sum. I came up with the following code that works, but will need a digit (0 ideally. Any other number will increase the result by that number.) to be provided when it is called.

list_sum([], X):- write(X). 
list_sum([Head|Tail], X)  :- 
    K is X,
    W is K + Head, 
    list_sum(Tail, W).

The issue with the code now is that if you enter 1 instead of 0 as the initial variable as mentioned above, it increases the result by 1. I am not totally satisfied with the result to be honest.

Alm4my
  • 33
  • 3
  • 1
    Tip: don't mix *computation* with *output*. If you want to print a successful goal bindings, you can always call e.g. `g(X), write(X)`. – Paulo Moura Feb 22 '20 at 21:36