I'm working with Terms and Signatures (first-order predicate logic) in Prolog.
I've been given the signature (∅, Z, {add(2), sub(2), mult(2)})
. So there's no variables, the constants are integers, and the functions are "add", "sub" and "mult".
The goal is to write the predicate "calc/2", which takes a term (given the signature above) and calculates it according the usual definition of addition, subtraction and multiplication of integers. Example:
?- calc( add(sub(8,2), mult(4,-3)), N).
N = -6
(since ((8 − 2) + (4 ∗ −3)) = −6)
The problem is similar and related to this question, but one big difference is that I need to avoid using the =.. operator to find the name of the functions. Instead, I want to take advantage of the fact that Prolog uses pattern matching on a rule's parameters, which is what I'm completely stuck on. Another thing to note is that "add", "sub", and "mult" are not supposed to be predicates.
My first solution using the =.. operator was (with the help of the answer from the thread linked above):
op(+, add).
op(-, sub).
op(*, mult).
calc(Term, N) :-
calc2(Term, Expr),
N is Expr.
calc2(N,N) :- integer(N).
calc2(Term, Expr) :-
Term =.. [Op_str, Comp1, Comp2],
op(Op, Op_str),
calc2(Comp1, Expr1),
calc2(Comp2, Expr2),
Expr =.. [Op, Expr1, Expr2].
But since this^ isn't the type of solution I'm looking for it doesn't really help much with my issue.
I can pretty easily perform single calculations (like calc(add(1,2), N)
), but I struggle with anything more complicated than that.
To use pattern matching and avoid the univ operator =.., you can rewrite calc2/2 like this:
calc2(add(Op1, Op2), Expr1 + Expr2) :-
calc2(Op1, Expr1),
calc2(Op2, Expr2).
calc2(sub(Op1, Op2), Expr1 - Expr2) :-
calc2(Op1, Expr1),
calc2(Op2, Expr2).
calc2(mult(Op1, Op2), Expr1 * Expr2) :-
calc2(Op1, Expr1),
calc2(Op2, Expr2).
This makes the code more visual, where additional cases (e.g. power) can be handled by separate clauses. In addition, modern compilers can optimize the generated code.