Search code examples
prologfailure-sliceinstantiation-error

Argument is not instantiated, need it to start at zero but also be able to change it


Whenever I run my code, I get an error that the arguments are not instantiated.

ads(X,Z):- mod(X,2) =:= 0, Z is Z+X.
ads(X,Z) :- mod(N,2) =\= 0,Z is Z.

sum_of_nums(0,0,0).
sum_of_nums(X,Y,Z) :-  X=<Y, ad(X,Z), sum_of_nums(X+1,Y,Z).

I want to be able to add numbers from X to Y but only the even ones. Don't know why it doesn't work.


Solution

  • First, there are some tiny errors, your compiler should indicate, like the unnecessary singleton variables in the second clause. After repairing this, and replacing ads by ad we have:

    ad(X,Z):- mod(X,2) =:= 0, Z is Z+X.
    ad(X,Z) :- mod(X,2) =\= 0,Z is Z.
    
    sum_of_nums(0,0,0).
    sum_of_nums(X,Y,Z) :-  X=<Y, ad(X,Z), sum_of_nums(X+1,Y,Z).
    
    ?- sum_of_nums(1,2,S).
       error(instantiation_error,(is)/2).
    

    To locate this error, I will insert some goals false such that the resulting program still produces this instantiation error.

    ad(X,Z):- mod(X,2) =:= 0, Z is Z+X, false.
    ad(X,Z) :- false, mod(X,2) =\= 0,Z is Z.
    
    sum_of_nums(0,0,0) :- false.
    sum_of_nums(X,Y,Z) :-  X=<Y, ad(X,Z), false, sum_of_nums(X+1,Y,Z).
    
    ?- sum_of_nums(1,2,S).
       error(instantiation_error,(is)/2).
    

    Therefore, you have an error in this part already.

    It's the Z is Z+X. On the right hand side of (is)/2 you always need to have variables that are instantiated (known). And Z is not known.

    Variables are a bit different in Prolog. You cannot reassign them.

    And also, writing this in Prolog directly doesn't really show what the language is good at.

    sum_of(X, Y, 0) :-
       X > Y.
    sum_of(X1, Y, Z1) :-
       X1 =< Y,
       ( X1 mod 2 =:= 0 -> S = X1 ; S = 0 ),
       X2 is X1+1,
       sum_of(X2, Y, Z2),
       Z1 is Z2+S.
    

    A more idiomatic way would be:

    ?- between(1,6,N).
       N = 1
    ;  N = 2
    ;  N = 3
    ;  N = 4
    ;  N = 5
    ;  N = 6.
    ?- between(1,6,N), N mod 2 =:= 0.
       N = 2
    ;  N = 4
    ;  N = 6.
    ?- findall(N, ( between(1,6,N), N mod 2 =:= 0 ), Ns).
       Ns = [2,4,6].
    ?- findall(N, ( between(1,6,N), N mod 2 =:= 0 ), Ns), sum_list(Ns,Sum).
       Ns = [2,4,6], Sum = 12.
    
    sum_of(X,Y,Sum) :-
       findall(N, ( between(X,Y,N), N mod 2 =:= 0 ), Ns),
       sum_list(Ns,Sum).