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