Mathematica Programming » Code Structure

Patterns

Simple Patterns

Patterns and how to use them might be the most important thing to understand in Mathematica

At some level, due to its expression-centered structure, Mathematica is just a huge pattern matching machine

The most basic pattern is just an underscore, but of course it has an expression structure:

 _ // FullForm
 Blank[]

We can also provide a name for our pattern which has in various function

 p_//FullForm
 Pattern[p,Blank[]]

You'll notice the Blank[] remains. Blank[] matches any single expression. For pattern matching there's the function MatchQ . It will return whether the expression as in its first argument matches the pattern in its second.

It doesn't have to have a pattern as its second argument though:

 MatchQ[a,a]
 True

But the following will also match:

 MatchQ[a,_]
 True

As will the following where we simply named one of the parameters:

 MatchQ[a, p_]
 True

Patterns of this type can also have a Head that must match, which is specified after the underscore.

 _a//FullForm
 Blank[a]

Any expression with a as its Head will match this pattern. For instance an empty invocation:

 MatchQ[a[], _a]
 True

With arguments

 MatchQ[a[1,2,3], _a]
 True

Or incredibly deeply nested:

 MatchQ[a[a[a[a[a[a[]]]]]], _a]
 True

But the following won't:

 MatchQ[a, _a]
 False

Along with a head one can also specify a test condition which will be applied to the thing that's being matched against

 _a?test//FullForm
 PatternTest[Blank[a],test]

Which means the following will match:

 MatchQ[a[1,2,3],_a?(Length@#>1&)]
 True

But the other cases will not. Here we clearly have too few arguments:

 MatchQ[a[], _a?(Length@#>1&)]
 False

Here it's hard to see, but the outer-most structure has only one argument, so it also won't match:

 MatchQ[a[a[a[a[a[a[]]]]]],_a?(Length@#>1&)]
 False

Patterns can also be specified as parts of an expression:

 MatchQ[a[1,2,3],a[_,_,_]]
 True

Patterns can also be specified as matching 1 or more elements with a double underscore:

 __//FullForm
 BlankSequence[]

Since we have arguments this matches:

 MatchQ[a[1,2,3], a[__]]
 True

But this does not:

 MatchQ[a[], a[__]]
 False

or 0 or more elements via a triple underscore:

 ___//FullForm
 BlankNullSequence[]

This will match since all of these arguments have head Integer :

 MatchQ[a[1,2,3], a[___Integer]]
 True

And this will match since there aren't any arguments anyway:

 MatchQ[a[],a[___]]
 True

Everything that applies to single element patterns applies as well to these multi-element patterns.

Complex Patterns

Often it's useful to be able to match more than one type. For this, there's a built-in head Alternatives , which is represented by a vertical bar |

An Integer matches this pattern:

 MatchQ[10, _Integer|_String]
 True

As does a String :

 MatchQ["10", _Integer|_String]
 True

But a Symbol does not:

 MatchQ[π, _Integer|_String]
 False

Alternatives can be used with any pattern and simplifies life tremendously in describing complicated structures concisely

One issue, however, is that a set of alternatives can't be matched multiple times with the double or triple underscores. To fix this issue there is Repeated ( .. ) and RepeatedNull ( ... ) which have the same meanings as __ and ___ respectively.

Here's an example of matching a complex structure:

 MatchQ[RandomChoice[{A,B,C}]@(Sequence@@(
  If[RandomInteger[]==0,ToString@#,#]&/@RandomInteger[10,RandomInteger[10]]
  )),
  (A|B|C)[(_String|_Integer)...]
]
 True

It is impossible to know ahead of time what the head will be or how many arguments there will be or whether they'll be strings or integers, but that doesn't matter. We can still represent this pattern in under one line. Yet the pattern is robust enough that by doing something like appending in a symbol, our pattern catches that:

 MatchQ[
  Append[
    RandomChoice[{A,B,C}]@(
      Sequence@@(
        If[RandomInteger[]==0,ToString@#,#]&/@RandomInteger[10,RandomInteger[10]]
        )
      ),
    RandomChoice[{A,B,C}]
    ],
  (A|B|C)[(_String|_Integer)...]
  ]
 False

There is one further useful addition to this current redux on patterns. Sometime you know you'll, say, see pairs of a patterns, and so a simple .. or __ isn't precise enough. For this case there is the head PatternSequence which can capture this:

 MatchQ[
  A@@(If[OddQ@#,ToString@#,#]&/@Range[10]),
  A[PatternSequence[_String,_Integer]..]
  ]
 True

Except

The final piece of patterns is the head Except . It specifies that anything Except the pattern inside should match. It is a negation pattern, in essence.

 MatchQ[1,Except[_String]]
 True

These examples show a basic usage:

 MatchQ[<|a->b|>, Except[_String]]
MatchQ[Graphics@Disk[], Except[_String]]
MatchQ["asdasd", Except[_String]]
 True
 True
 False

One interesting case is the two argument form of Except , where the second argument is a pattern that should match and the first argument is a list of exceptions to that pattern.