/* SIMPLIFY.
The aim of the predicate simplify is to reshuffle expressions which
are sums of unknowns (represented by Prolog atoms) and integers so
that all the unknowns are at the start and the sum of all the
integers is at the end, e.g.
x+3+y+4+z gets reshuffled to x+y+z+7
This is (a) useful in mathematical manipulations by computer
and (b) an illustration of a useful technique called
"difference pairs".
To summarise the idea: a structure such as +(a,+(b,c)) could be
drawn as
+
/ \
a +
/ \
b c
The idea is that when constructing such things you can represent
intermediate stages as the "difference" of
+
/ \
a +
/ \
b Zend
and the variable Zend. To move to the next stage, you just have
to instantiate Zend as something; if the next stage is also
intermediate, why not another "difference pair", e.g. Zend =
+
/ \
c Zend2
with Zend2 uninstantiated, and so on.
The predicate normalise defined below shows the idea in practice:
in normalise(Formula,V,Vend,N,Nend)
the unknowns (Prolog atoms) are collected in the difference pair
of V and Vend, and the integers are collected in the difference
pair of N and Nend. The predicate simplify tidies up the final
answer by instantiating the variable end of the structure in which
the unknowns are collected to the structure in which the integers
were collected. Note that in the body of simplify, the last argument
of the call of normalise is 0 - this instantiates the variable at the
trailing end of the integer-collecting structure, so that the goal
Vend is N
succeeds and evaluates the structure as the sum of all the integers!
*/
simplify(Formula,V) :-
normalise(Formula,V,Vend,N,0),
Vend is N.
normalise(A+B,V,Vend,N,Nend) :-
!,
normalise(A,V,Vsofar,N,Nsofar),
normalise(B,Vsofar,Vend,Nsofar,Nend).
normalise(Atom,Atom+Vend,Vend,N,N) :-
atom(Atom),
!.
normalise(Integer,V,V,Integer+N,N) :-
integer(Integer).