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.