Search code examples
prologsignaturepredicatefirst-order-logic

first order logic creating terms for arithmetic expressions using prolog


given a signature (0,Z,{plus(2),minus(2),times(2)}, constants are integers and functions are plus, minus and times with arity 2 for each. I wanted to write a two predicates arth/2 and printarth/1 which takes terms in the above signature and do the necessary arithmetic calculations addition, subtraction and multiplication.arth/2 will print the results and printarth/1 should results out the evaluation expression as shown below.

I wanted to achieve two things

first:

?- arth( plus(minus(8,2), times(4,-3)), N).
N = -6

N is evaluated as ((8−2) + (4∗−3)) = (6 +−12) =−6

second:

?- printarth(plus(minus(8,2), times(4,-3)), N).
((8 - 2) + (4 * -3))
true.

I understand that the use of Terms, Ops and complex terms are used for this and started my code as below

arithmetic_operator('+').
arithmetic_operator('-').
arithmetic_operator('*').

arithmetic_expression(N) :- integer(N).

arithmetic_expression(Term) :-
    Term =..[Functor,Component1,Component2],
    arithmetic_operator(Functor),
    arithmetic_expression(Component1),
    arithmetic_expression(Component2).

From here I find it difficult on how to create arth/2 and printarth/1 as I cannot call arithmetic_expression(Term) and throws me an error when I call it.

?- arithmetic_expression(..[+,5,7]).
ERROR: Syntax error: Operator expected
ERROR: arithmetic_expression(.
ERROR: ** here **
ERROR: .[+,5,7]) .

any resources on this task is very useful.


Solution

  • If you want to take a term that looks like this:

    minus(2, 3)
    

    and turn it into an arithmetic expression -(2, 3) which is equivalent to 2 - 3 (with the default definition of - as an operator), then evaluate it, you could do it like this:

    term_arithmetic_expression(T, E) :-
        T =.. [Name, X, Y],
        binary_op(Name, Op),
        E =.. [Op, X, Y].
    
    eval_arithmetic_expression(T, R) :-
        term_arithmetic_expression(T, E),
        R is E.
    
    binary_op(minus, -).
    % add more binary operations
    

    Now this at least works:

    ?- eval_arithmetic_expression(minus(2, 3), R).
    R = -1.
    

    As you see, both term_arithmetic_expression/2 and eval_arithmetic_expression/2 have two arguments. This is what you need to map minus(2, 4) to 2 - 4.

    Your arithmetic_expression/1 is correctly traversing, but not mapping from the one representation to the other. Your arithmetic_operator has the same problem. With minimal changes:

    arithmetic_operator(plus, +).
    arithmetic_operator(minus, -).
    arithmetic_operator(times, *).
    
    arithmetic_expression(N, N) :- integer(N).
    
    arithmetic_expression(Term, Expr) :-
        Term =.. [Functor,Component1,Component2],
        arithmetic_operator(Functor, Operator),
        arithmetic_expression(Component1, Expr1),
        arithmetic_expression(Component2, Expr2),
        Expr =.. [Operator, Expr1, Expr2].
    

    and then:

    ?- arithmetic_expression(plus(minus(8,2), times(4,-3)), Expr).
    Expr = 8-2+4* -3 ;
    false.
    
    ?- arithmetic_expression(plus(minus(8,2), times(4,-3)), Expr),
       Result is Expr.
    Expr = 8-2+4* -3,
    Result = -6 ;
    false.
    
    ?- arithmetic_expression(plus(minus(8,2), times(4,-3)), Expr),
       Result is Expr,
       display(Expr).
    +(-(8,2),*(4,-3))
    Expr = 8-2+4* -3,
    Result = -6 ;
    false.
    

    The display is what is outputting +(-(8,2),*(4,-3)) in the last query.