Next: Nested Formulas with Rewrite Rules, Previous: Other Features of Rewrite Rules, Up: 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

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

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

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
disastrous: 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!