简体   繁体   中英

Sum numbers up to N-M in Prolog

Hey I need to make a function countNM(N,M,S) that sums all the numbers up to NM (and returns 0 if N is equal to or less than M). I have worked it out and what I have makes sense when I work through it in my head, but something is uninstantiated...

sumNM(0,0,0).
sumNM(N,M,S):-
    N=<M,
    S is 0.
sumNM(N,M,S):-
    N>M,
    A is N-M,
    B is S+A,
    C is M+1,
    sumNM(N,C,B).

The problem is on the line

B is S+A,

At this point, S is not bound, causing the arithmetic expression to fail.

Other than that, the logic behind this expression is also wrong. What you're saying is that the sum of one fewer number (B) is equal to the total sum (S) plus the current number (A). This should be the other way around.

You can replace the last predicate with this code:

sumNM(N,M,S):-
    N>M,
    A is N-M,
    C is M+1,
    sumNM(N,C,B),
    S is B+A.

Here we first calculate the sum B, after which we add A to it, resulting in the total sum S.

This is an unusual problem.

While @Steven found the principal problem there are other issues. Namely, you have four clauses when two will do. ( Edit : I misread the question due to improper indentation, as mentioned by @Steven below. The "fourth clause" does not exist.) You can delete the first and fourth: sumNM(0,0,0) actually matches two of the other clauses (0 =< 0 after all) and the fourth clause is an "anything goes" clause that probably shouldn't be there at all. For instance, sumNM(50,45,N) yields N = 15 and sumNM(50,45,15) unifies once and then winds up creating an exception because N is unbound. Why? Because the last clause basically passes anything. sumNM(50,45,10000000) also unifies and that seems obviously untrue. But sumNM(_,_,_) means "any three things are related by sumNM/3 ." And there's no difference between sumNM(N,C,B) and sumNM(_,_,_) : that's why you're getting the singleton variable warning. If you find this surprising, it points to a misunderstanding about the nature of variables somewhere.

In cases like this where you really have an exclusion it's probably simplest (and leads to fewer spurious choice points/solutions) to use an if/else construct. I would therefore recommend the following implementation:

sumNM(N,M,S):-
    (
        N =< M
    ->
        S is 0
    ;
        A is N-M,
        C is M+1,
        sumNM(N,C,B),
        S is B+A
    ).

Note that this implementation is still only partially correct! This now works (and notice we do not get prompted for additional solutions, without having to employ the cut):

?- sumNM(50,45,15).
true.
?- sumNM(50,45,20000000).
false.

But this still doesn't:

?- sumNM(50,X,Y).
ERROR: =</2: Arguments are not sufficiently instantiated

The situation could probably be corrected by use of clpfd but I won't be able to help you get there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM