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

Go to the first, previous, next, last section, table of contents.