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
(*Out:*)
Blank[]
We can also provide a name for our pattern which has in various function
p_//FullForm
(*Out:*)
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]
(*Out:*)
True
But the following will also match:
MatchQ[a,_]
(*Out:*)
True
MatchQ[a,p_]
(*Out:*)
True
Patterns can also have a Head
that must match, which is specified after the underscore.
_a//FullForm
(*Out:*)
Blank[a]
With a head all the following match:
MatchQ[a[],_a]
(*Out:*)
True
MatchQ[a[1,2,3],_a]
(*Out:*)
True
MatchQ[a[a[a[a[a[a[]]]]]],_a]
(*Out:*)
True
But the following won’t:
MatchQ[a,_a]
(*Out:*)
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
(*Out:*)
PatternTest[Blank[a],test]
Which means the following will match:
MatchQ[a[1,2,3],_a?(Length@#>1&)]
(*Out:*)
True
But the other cases will not:
MatchQ[a[],_a?(Length@#>1&)]
(*Out:*)
False
MatchQ[a[a[a[a[a[a[]]]]]],_a?(Length@#>1&)]
(*Out:*)
False
Patterns can also be specified as parts of an expression:
MatchQ[a[1,2,3],a[_,_,_]]
(*Out:*)
True
Patterns can also be specified as matching 1 or more elements with a double underscore:
__//FullForm
(*Out:*)
BlankSequence[]
MatchQ[a[1,2,3],a[__]]
(*Out:*)
True
MatchQ[a[],a[__]]
(*Out:*)
False
or 0 or more elements via a triple underscore:
___//FullForm
(*Out:*)
BlankNullSequence[]
MatchQ[a[1,2,3],a[___Integer]]
(*Out:*)
True
MatchQ[a[],a[___]]
(*Out:*)
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 |
MatchQ[10,_Integer|_String]
(*Out:*)
True
MatchQ["10",_Integer|_String]
(*Out:*)
True
MatchQ[π,_Integer|_String]
(*Out:*)
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 complex structure:
MatchQ[RandomChoice[{A,B,C}]@(Sequence@@(
If[RandomInteger[]==0,ToString@#,#]&/@RandomInteger[10,RandomInteger[10]]
)),
(A|B|C)[(_String|_Integer)...]
]
(*Out:*)
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)...]
]
(*Out:*)
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]..]
]
(*Out:*)
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]]
(*Out:*)
True
MatchQ[<|a->b|>,Except[_String]]
(*Out:*)
True
MatchQ[Graphics@Disk[],Except[_String]]
(*Out:*)
True
MatchQ["asdasd",Except[_String]]
(*Out:*)
False