It is possible to get Calc to apply a set of rewrite rules on all
results, effectively adding to the built-in set of default
simplifications. To do this, simply store your rule set in the
variable EvalRules
. There is a convenient s E command
for editing EvalRules
; see section Other Operations on Variables.
For example, suppose you want `sin(a + b)' to be expanded out to `sin(b) cos(a) + cos(b) sin(a)' wherever it appears, and similarly for `cos(a + b)'. The corresponding rewrite rule set would be,
[ sin(a + b) := cos(a) sin(b) + sin(a) cos(b), cos(a + b) := cos(a) cos(b) - sin(a) sin(b) ]
To apply these manually, you could put them in a variable called
trigexp
and then use a r trigexp every time you wanted
to expand trig functions. But if instead you store them in the
variable EvalRules
, they will automatically be applied to all
sines and cosines of sums. Then, with `2 x' and `45' on
the stack, typing + S will (assuming degrees mode) result in
`0.7071 sin(2 x) + 0.7071 cos(2 x)' automatically.
As each level of a formula is evaluated, the rules from
EvalRules
are applied before the default simplifications.
Rewriting continues until no further EvalRules
apply.
Note that this is different from the usual order of application of
rewrite rules: EvalRules
works from the bottom up, simplifying
the arguments to a function before the function itself, while a r
applies rules from the top down.
Because the EvalRules
are tried first, you can use them to
override the normal behavior of any built-in Calc function.
It is important not to write a rule that will get into an infinite
loop. For example, the rule set `[f(0) := 1, f(n) := n f(n-1)]'
appears to be a good definition of a factorial function, but it is
unsafe. Imagine what happens if `f(2.5)' is simplified. Calc
will continue to subtract 1 from this argument forever without reaching
zero. A safer second rule would be `f(n) := n f(n-1) :: n>0'.
Another dangerous rule is `g(x, y) := g(y, x)'. Rewriting
`g(2, 4)', this would bounce back and forth between that and
`g(4, 2)' forever. If an infinite loop in EvalRules
occurs, Emacs will eventually stop with a "Computation got stuck
or ran too long" message.
Another subtle difference between EvalRules
and regular rewrites
concerns rules that rewrite a formula into an identical formula. For
example, `f(n) := f(floor(n))' "fails to match" when n is
already an integer. But in EvalRules
this case is detected only
if the righthand side literally becomes the original formula before any
further simplification. This means that `f(n) := f(floor(n))' will
get into an infinite loop if it occurs in EvalRules
. Calc will
replace `f(6)' with `f(floor(6))', which is different from
`f(6)', so it will consider the rule to have matched and will
continue simplifying that formula; first the argument is simplified
to get `f(6)', then the rule matches again to get `f(floor(6))'
again, ad infinitum. A much safer rule would check its argument first,
say, with `f(n) := f(floor(n)) :: !dint(n)'.
(What really happens is that the rewrite mechanism substitutes the
meta-variables in the righthand side of a rule, compares to see if the
result is the same as the original formula and fails if so, then uses
the default simplifications to simplify the result and compares again
(and again fails if the formula has simplified back to its original
form). The only special wrinkle for the EvalRules
is that the
same rules will come back into play when the default simplifications
are used. What Calc wants to do is build `f(floor(6))', see that
this is different from the original formula, simplify to `f(6)',
see that this is the same as the original formula, and thus halt the
rewriting. But while simplifying, `f(6)' will again trigger
the same EvalRules
rule and Calc will get into a loop inside
the rewrite mechanism itself.)
The phase
, schedule
, and iterations
markers do
not work in EvalRules
. If the rule set is divided into phases,
only the phase 1 rules are applied, and the schedule is ignored.
The rules are always repeated as many times as possible.
The EvalRules
are applied to all function calls in a formula,
but not to numbers (and other number-like objects like error forms),
nor to vectors or individual variable names. (Though they will apply
to components of vectors and error forms when appropriate.) You
might try to make a variable phihat
which automatically expands
to its definition without the need to press = by writing the
rule `quote(phihat) := (1-sqrt(5))/2', but unfortunately this rule
will not work as part of EvalRules
.
Finally, another limitation is that Calc sometimes calls its built-in
functions directly rather than going through the default simplifications.
When it does this, EvalRules
will not be able to override those
functions. For example, when you take the absolute value of the complex
number (2, 3), Calc computes `sqrt(2*2 + 3*3)' by calling
the multiplication, addition, and square root functions directly rather
than applying the default simplifications to this formula. So an
EvalRules
rule that (perversely) rewrites `sqrt(13) := 6'
would not apply. (However, if you put Calc into symbolic mode so that
`sqrt(13)' will be left in symbolic form by the built-in square
root function, your rule will be able to apply. But if the complex
number were (3,4), so that `sqrt(25)' must be calculated,
then symbolic mode will not help because `sqrt(25)' can be
evaluated exactly to 5.)
One subtle restriction that normally only manifests itself with
EvalRules
is that while a given rewrite rule is in the process
of being checked, that same rule cannot be recursively applied. Calc
effectively removes the rule from its rule set while checking the rule,
then puts it back once the match succeeds or fails. (The technical
reason for this is that compiled pattern programs are not reentrant.)
For example, consider the rule `foo(x) := x :: foo(x/2) > 0'
attempting to match `foo(8)'. This rule will be inactive while
the condition `foo(4) > 0' is checked, even though it might be
an integral part of evaluating that condition. Note that this is not
a problem for the more usual recursive type of rule, such as
`foo(x) := foo(x/2)', because there the rule has succeeded and
been reactivated by the time the righthand side is evaluated.
If EvalRules
has no stored value (its default state), or if
anything but a vector is stored in it, then it is ignored.
Even though Calc's rewrite mechanism is designed to compare rewrite
rules to formulas as quickly as possible, storing rules in
EvalRules
may make Calc run substantially slower. This is
particularly true of rules where the top-level call is a commonly used
function, or is not fixed. The rule `f(n) := n f(n-1) :: n>0' will
only activate the rewrite mechanism for calls to the function f
,
but `lg(n) + lg(m) := lg(n m)' will check every `+' operator.
And `apply(f, [a*b]) := apply(f, [a]) + apply(f, [b]) ::
in(f, [ln, log10])' may seem more "efficient" than two separate
rules for ln
and log10
, but actually it is vastly less
efficient because rules with apply
as the top-level pattern
must be tested against every function call that is simplified.
Suppose you want `sin(a + b)' to be expanded out not all the time,
but only when a s is used to simplify the formula. The variable
AlgSimpRules
holds rules for this purpose. The a s command
will apply EvalRules
and AlgSimpRules
to the formula, as
well as all of its built-in simplifications.
Most of the special limitations for EvalRules
don't apply to
AlgSimpRules
. Calc simply does an a r AlgSimpRules
command with an infinite repeat count as the first step of a s.
It then applies its own built-in simplifications throughout the
formula, and then repeats these two steps (along with applying the
default simplifications) until no further changes are possible.
There are also ExtSimpRules
and UnitSimpRules
variables
that are used by a e and u s, respectively; these commands
also apply EvalRules
and AlgSimpRules
. The variable
IntegSimpRules
contains simplification rules that are used
only during integration by a i.