MathGroup Archive 1998

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Pattern matching trivia

  • To: mathgroup at
  • Subject: [mg13651] Re: Pattern matching trivia
  • From: Robert Villegas <villegas>
  • Date: Fri, 07 Aug 1998 17:22:00 -0500
  • Organization: Wolfram Research
  • Sender: owner-wri-mathgroup at

> I just discovered something interesting about pattern matching. The
> definitions for (goo) are no applied in the lines below.   I understand
> this is because an expression with the head Complex or Rational is an
> atom, and is  not considered a sum or product of smaller expressions.
> In[1]:=
> goo[a_+b_]:={Complex,a,b}
> goo[m_/n_]:={Rational,m,n}
> In[3]:=
> goo[2+3 I]
> Out[3]=
> goo[2+3 I]
> In[4]:=
> goo[2/3]//InputForm
> Out[4]=
> goo[2/3]

Although rational and complex numbers are treated as atoms by most
functions, e.g. AtomQ returns True for them, it is possible to take
them apart with pattern-matching.  If you defined goo as follows:

In[1]:= goo[Rational[a_, b_]] := {Rational, a, b}

In[2]:= goo[Complex[a_, b_]] := {Complex, a, b}

Then it would work on these inputs:

In[3]:= goo[2/3]

Out[3]= {Rational, 2, 3}

In[4]:= goo[3 + 5 I]

Out[4]= {Complex, 3, 5}

In fact, goo could be defined with a more generic pattern to deconstruct
any normal, headed expression into its parts:

In[5]:= ClearAll[goo]

In[6]:= goo[ h_[args___] ] := {h, {args}}

In[7]:= goo[2/3]

Out[7]= {Rational, {2, 3}}

In[8]:= goo[3 + 5 I]

Out[8]= {Complex, {3, 5}}

> Now the function (foo) below has the attribute HoldAllComplete. The
> peculiar thing is that the definitions are now used.  It seems that when
> the arguments of (foo) are Complex or Rational, the pattern matcher now
> treats these arguments as the sum or product of smaller expressions.
> In[5]:=
> Attributes[foo]={HoldAllComplete};
> foo[a_+b_]:={Complex,a,b}
> foo[m_/n_]:={Rational,m,n}
> In[7]:=
> foo[2+3I]
> Out[7]=
> {Complex,2,3 I}
> In[8]:=
> foo[2/3]
> Out[8]=
> {Rational,2,3}

It turns out that there is nothing peculiar going on here.  Since foo
has one of the hold attributes (HoldFirst or HoldAll would have done
just as well), its arguments are not evaluated before it receives them.
Hence, when you type the input 'foo[2 + 3 I]', foo will receive the
argument '2 + 3 I' straight out of the parser, before the evaluator
gets a chance to touch it.  To see the parsed, unevaluated form of any
input, here is a little idiom I use:


For example:

In[9]:= HoldForm @ FullForm[3 + 4 I]

Out[9]= Plus[3, Times[4, I]]

In[10]:= HoldForm @ FullForm[2/3]

Out[10]= Times[2, Power[3, -1]]

In[11]:= HoldForm @ FullForm[ Evaluate[Sequence[2^3, 3 + 4]] ]

Out[11]= Evaluate[Sequence[Power[2, 3], Plus[3, 4]]]

Knowing what the unevaluated structure of the input looks like, it's
easy to see why foo received a form it expected, while goo did not.

Also, it follows that any Hold* attribute isn't vital; what is important
is that goo receive an unevaluated argument.  Even when a function
doesn't have any of the Hold* attributes, you can still pass it
particular arguments sans evaluation by using the argument wrapper
Unevaluated.  Here is your original definition of goo, given explicitly
unevaluated arguments:

In[70]:= goo[ Unevaluated[2 + 3 I] ]

Out[70]= {Complex, 2, 3 I}

In[71]:= goo[ Unevaluated[2/3] ]

Out[71]= {Rational, 2, 3}

> Note:  Parenthesize is only documented via the usage message below. I
> think that means it's an experimental feature.
> In[9]:=
> ?Parenthesize
> "Parenthesize[ expr, fmt, prec, group] will represent expr in format fmt
> and  \
> parenthesize it if necessary."

It may or may not continue in its present form, but its functionality is
important, so it will continue to exist in some form.

When you create a typeset form for a function or operator, you must
write a MakeBoxes definition for that function.  Almost always, your
definition must typeset the arguments of the function.  If an argument
is itself some complex expression, then depending on what its outermost
operator is, it may need to be parenthesized in order to look right in
the context of the overall typeset form.

For example, if you want Transpose[A] to have the typeset form


then you might, erroneously, write it this way:

  Transpose /: MakeBoxes[Transpose[matrix_], TraditionalForm] :=
    SuperscriptBox[MakeBoxes[matrix, TraditionalForm], "T"]

This is erroneous because Transpose[A + B] would typeset as

   A + B

which looks like A + Transpose[B], since superscripting by convention
has higher precedence than addition.  You want it to typeset as

   (A + B)

To achieve this, the typesetting of 'matrix' in your definition must be
informed of the context in which 'matrix' occurs, so that it can decide
if the outermost operator in 'matrix' has a low enough precedence that
it must be parenthesized.

Parenthesize fits this bill, taking the same first two arguments as
MakeBoxes, and taking additional arguments to specify what the
enclosing operator is, and perhaps also on which side of it the
expression- to-be-typeset falls.  The correct rule for Transpose is:

  Transpose /: MakeBoxes[ Transpose[list_], TraditionalForm] :=
      Parenthesize[list, TraditionalForm, Power, Left],

This function probably ought to be mentioned somewhere, since it is
important, even if it is replaced with something better later.

Robby Villegas

  • Prev by Date: Re: Undocumented 3.0 Features
  • Next by Date: Re: What is the fastest machine for Mathematica?
  • Previous by thread: Re: Pattern matching trivia
  • Next by thread: RE: Re: Undocumented 3.0 Featu