Next: Nested Formulas with Rewrite Rules, Previous: Other Features of Rewrite Rules, Up: Rewrite Rules [Contents][Index]

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

Next: Nested Formulas with Rewrite Rules, Previous: Other Features of Rewrite Rules, Up: Rewrite Rules [Contents][Index]