Search code examples
prolog

Prolog - confused about return results of recursive rule


I'm playing around with recursion in Prolog, and I'm confused. I am trying to write rules that can determine if a number is even or odd. I know that there are other stackoverflow questions about this, but I don't care about having a working solution, I am more interested in knowing why mine doesn't work.

Here are my rules:

even(0).
even(N) :- N>0, N1 is N-1, odd(N1).
odd(N) :- N>0, N1 is N-1, even(N1).

When I query even(0), I get returned 2 results. The first result is true, the 2nd is false. This also happens with odd(1), even(2), odd(3), etc. Why am I getting 2 return results? Shouldn't I just get 1?


Solution

  • When you query even(0), it succeeds as you have seen. But you've also seen it prompts you for more results because it left a choicepoint, which is a place in the logic where Prolog decides it can come back and explore other alternatives for a potentially successful query. Upon going back to the choicepoint and attempting to find more solutions, it does not find more, so it comes back "false" since it found no more solutions. So it did just find one solution, but the choice point caused backtracking after which it found no additional solutions. This is the case with your other successful queries as well.

    You'll note that if you make a more general query, it gives an error (example taken from GNU Prolog):

    | ?- even(N).
    
    N = 0 ? ;
    uncaught exception: error(instantiation_error,(>)/2)
    | ?-
    

    This is because you are using specific arithmetic expression operators that require that the variables be instantiated. These are relational operators like (>)/2 and the is/2 operator. You can make the solution more relational by using the CLP(FD) operators which are designed for reasoning with integers:

    even(0).
    even(N) :-
        N #> 0,
        N1 #= N-1,
        odd(N1).
    odd(N) :-
        N #> 0,
        N1 #= N-1,
        even(N1).
    

    Then you get a more general solution, which is more complete and more useful:

    | ?- even(N).
    
    N = 0 ? ;
    
    N = 2 ? ;
    
    N = 4 ? ;
    
    N = 6 ? ;
    ...
    
    | ?- odd(N).
    
    N = 1 ? ;
    
    N = 3 ? ;
    
    N = 5 ? ;
    
    N = 7 ?
    ...
    


    If you know there is at most one answer, or if you only care about the first possible answer, you can use once/1 (examples taken from SWI Prolog here):

    2 ?- even(2).
    true ;
    false.
    
    3 ?- once(even(2)).
    true.
    
    4 ?- even(N).
    N = 0 ;
    N = 2 ;
    N = 4 ;
    ...
    
    5 ?- once(even(N)).
    N = 0.
    
    6 ?-
    

    As expected, once(even(N)) terminates after finding the first solution.