Go forward to Nested Formulas with Rewrite Rules. Go backward to Other Features of Rewrite Rules. Go up to Rewrite Rules.

Composing Patterns in Rewrite Rules
-----------------------------------

There are three operators, `&&&', `|||', and `!!!', that combine
rewrite patterns to make larger patterns.  The combinations are "and,"
"or," and "not," respectively, and these operators are the pattern
equivalents of `&&', `||' and `!' (which operate on zero-or-nonzero
logical values).

Note that `&&&', `|||', and `!!!' are left in symbolic form by all
regular Calc features; they have special meaning only in the context
of rewrite rule patterns.

The pattern `P1 &&& P2' matches anything that matches both P1 and P2.
One especially useful case is when one of P1 or P2 is a meta-variable.
For example, here is a rule that operates on error forms:

     f(x &&& a +/- b, x)  :=  g(x)

This does the same thing, but is arguably simpler than, the rule

     f(a +/- b, a +/- b)  :=  g(a +/- b)

Here's another interesting example:

     ends(cons(a, x) &&& rcons(y, b))  :=  [a, b]

which effectively clips out the middle of a vector leaving just the
first and last elements.  This rule will change a one-element vector
`[a]' to `[a, a]'.  The similar rule

     ends(cons(a, rcons(y, b)))  :=  [a, b]

would do the same thing except that it would fail to match a
one-element vector.


The pattern `P1 ||| P2' matches anything that matches either P1 or P2.
Calc first tries matching against P1; if that fails, it goes on to try
P2.

A simple example of `|||' is

     curve(inf ||| -inf)  :=  0

which converts both `curve(inf)' and `curve(-inf)' to zero.

Here is a larger example:

     log(a, b) ||| (ln(a) :: let(b := e))  :=  mylog(a, b)

This matches both generalized and natural logarithms in a single rule.
Note that the `::' term must be enclosed in parentheses because that
operator has lower precedence than `|||' or `:='.

(In practice this rule would probably include a third alternative,
omitted here for brevity, to take care of `log10'.)

While Calc generally treats interior conditions exactly the same as
conditions on the outside of a rule, it does guarantee that if all the
variables in the condition are special names like `e', or already
bound in the pattern to which the condition is attached (say, if `a'
had appeared in this condition), then Calc will process this condition
right after matching the pattern to the left of the `::'.  Thus, we
know that `b' will be bound to `e' only if the `ln' branch of the
`|||' was taken.

Note that this rule was careful to bind the same set of meta-variables
on both sides of the `|||'.  Calc does not check this, but if you bind
a certain meta-variable only in one branch and then use that
meta-variable elsewhere in the rule, results are unpredictable:

     f(a,b) ||| g(b)  :=  h(a,b)

Here if the pattern matches `g(17)', Calc makes no promises about the
value that will be substituted for `a' on the righthand side.


The pattern `!!! PAT' matches anything that does not match PAT.  Any
meta-variables that are bound while matching PAT remain unbound
outside of PAT.

For example,

     f(x &&& !!! a +/- b, !!![])  :=  g(x)

converts `f' whose first argument is anything *except* an error form,
and whose second argument is not the empty vector, into a similar call
to `g' (but without the second argument).

If we know that the second argument will be a vector (empty or not),
then an equivalent rule would be:

     f(x, y)  :=  g(x)  :: typeof(x) != 7 :: vlen(y) > 0

where of course 7 is the `typeof' code for error forms.  Another final
condition, that works for any kind of `y', would be `!istrue(y ==
[])'.  (The `istrue' function returns an explicit 0 if its argument
was left in symbolic form; plain `!(y == [])' or `y != []' would not
work to replace `!!![]' since these would be left unsimplified, and
thus cause the rule to fail, if `y' was something like a variable
name.)

It is possible for a `!!!' to refer to meta-variables bound elsewhere
in the pattern.  For example,

     f(a, !!!a)  :=  g(a)

matches any call to `f' with different arguments, changing this to `g'
with only the first argument.

If a function call is to be matched and one of the argument patterns
contains a `!!!' somewhere inside it, that argument will be matched
last.  Thus

     f(!!!a, a)  :=  g(a)

will be careful to bind `a' to the second argument of `f' before
testing the first argument.  If Calc had tried to match the first
argument of `f' first, the results would have been disasterous: Since
`a' was unbound so far, the pattern `a' would have matched anything at
all, and the pattern `!!!a' therefore would *not* have matched
anything at all!