Re: Pattern matching trivia
- To: mathgroup at smc.vnet.net
- 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 wolfram.com
> 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: HoldForm[FullForm[input]] 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 T A 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 T A + B which looks like A + Transpose[B], since superscripting by convention has higher precedence than addition. You want it to typeset as T (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] := SuperscriptBox[ Parenthesize[list, TraditionalForm, Power, Left], "T" ] This function probably ought to be mentioned somewhere, since it is important, even if it is replaced with something better later. Robby Villegas