Search code examples
prologlogic-programming

Arithmetic operations in the head of a clause


I am a writing Prolog predicate that would compare some point (myPosition(2,2)) in Cartesian coordinate system with some other point in the neighbourhood. As a result, it should show the direction (north, east, south, west) that we should choose to get from myPosition(2,2) to that point. Here is the code of my .pl file:

myPosition(2,2).

turn_to_east(X+1,Y) :-
    myPosition(X,Y),
    write('East.').

turn_to_west(X-1,Y) :-
    myPosition(X,Y),
    write('West.').

turn_to_north(X,Y+1) :-
    myPosition(X,Y),
    write('North.').

turn_to_south(X,Y-1) :-
    myPosition(X,Y),
    write('South.').

turn_to_the_point(X,Y) :- turn_to_east(X,Y).
turn_to_the_point(X,Y) :- turn_to_west(X,Y).
turn_to_the_point(X,Y) :- turn_to_north(X,Y).
turn_to_the_point(X,Y) :- turn_to_south(X,Y).

Then, when I upload the file to SWI-Prolog and write:

turn_to_the_point(1,2).

I get just:

false.

Why can't I get the answer 'West.' or any other?


Solution

  • The reason is that by default Prolog does not interpret +, - and other arithmetic operators. 1+1 is simply 1+1 (this is syntactical sugar for +(1,1)), not 2. This could be useful if you for instance would like to define your own expression evaluator, and you see + as the boolean sum, or something completely different.

    There is however one way to collapse such expression such that it derives 2 out of 1+1 using the (is)/2 predicate. For instance:

    turn_to_east(NX,Y) :-
        myPosition(X,Y),
        NX is X+1,
        write('East.').
    

    Given you query turn_to_east/2 with turn_to_east(1,2), NX = 1. Now you fetch the myPosition/2 data: X = 2 and Y = 2. Prolog does an equivalence check in the meantime and sees that the Y-coordinate of turn_to_east/2 is the same as the one of myPosition. Next it collapses 2+1 to 3 and sees that this is not equivalent to NX = 1 so this predicate fails. But if you had queried turn_to_east(3,1) it would have succeeded and thus write East..

    If you modify your entire theory with the above discussed concept, like:

    myPosition(2,2).
    
    turn_to_east(NX,Y) :-
        myPosition(X,Y),
        NX is X+1,
        write('East.').
    
    turn_to_west(NX,Y) :-
        myPosition(X,Y),
        NX is X-1,
        write('West.').
    
    turn_to_north(X,NY) :-
        myPosition(X,Y),
        NY is Y+1,
        write('North.').
    
    turn_to_south(X,NY) :-
        myPosition(X,Y),
        NY is Y-1,
        write('South.').
    
    turn_to_the_point(X,Y) :- turn_to_east(X,Y).
    turn_to_the_point(X,Y) :- turn_to_west(X,Y).
    turn_to_the_point(X,Y) :- turn_to_north(X,Y).
    turn_to_the_point(X,Y) :- turn_to_south(X,Y).
    

    It answers the query correctly:

    ?- turn_to_the_point(1,2).
    West.
    true ;
    false.
    

    A note in general is that predicates better not have side-effects like write/1 stuff: this is not only for Prolog, almost all programming languages advice to split a program into calculation and interaction. Perhaps a better way to solve this, is to see the direction as an parameter:

    myPosition(2,2).
    
    turn_to_point(NX,Y,east) :-
        myPosition(X,Y),
        NX is X+1.
    turn_to_point(NX,Y,west) :-
        myPosition(X,Y),
        NX is X-1.
    
    turn_to_point(X,NY,north) :-
        myPosition(X,Y),
        NY is Y+1.
    
    turn_to_point(X,NY,south) :-
        myPosition(X,Y),
        NY is Y-1.
    
    turn_to_the_point(X,Y) :-
        turn_to_point(X,Y,D),
        write(D).
    

    In that case the turn_to_the_point/2 predicate is clearly an interaction predicate whereas its turn_to_the_point/3 variant does computations.