General
When you define a type based on a head, like
f[x_List, y_List]:=...
the test happens entirely in the pattern-matcher, not involving the main evaluator. I call such patterns "syntactic". Pattern tests on such patterns are usually faster or much faster. The reason is that all the matching happens entirely in the pattern-matcher, and the latter only needs to operate on the syntactic form of expression (FullForm), to establish the fact of the match. This can also be viewed as a strongest typing scheme available in Mathematica.
When you defined the patterns with ? (PatternTest) or /; (Condition), you invite the main evaluator to join the game. So when you define a function like
f[x_?ListQ, y_?NumericQ]:=...,
the pattern-matcher always calls the main evaluator in order to pattern-match these patterns. Because of this, such tests are more general, but also can be significantly slower. They can also induce side effects, through the predicate's code executed by the main evaluator - which can't happen with the _h - style patterns.
Performance
Here is a comparison with a built-in atomic type (strings):
chars = RandomChoice[CharacterRange["a", "z"], 100000];
MatchQ[chars, {___?StringQ}] // AbsoluteTiming
MatchQ[chars, {___String}] // AbsoluteTiming
(* {0.018108, True} *)
(* {0.001687, True} *)
We can see an order of magnitude speed difference. This becomes even worse when the testing function is a little more complex:
ClearAll[f];
symtest = f /@ Range[100000];
MatchQ[symtest, {___f}] // AbsoluteTiming
MatchQ[symtest, {___?(Function[Head[#] === f])}] // AbsoluteTiming
(* {0.002725, True} *)
(* {0.087275, True} *)
The difference is most dramatic for packed arrays, where, in addition to the usual difference explained above, many pattern-testing system functions have been specially overloaded on certain patterns, so that they perform a constant-time check:
tst = Range[10000000];
MatchQ[tst, {___Integer}] // AbsoluteTiming
MatchQ[tst, {___?IntegerQ}] // AbsoluteTiming
(* {5.*10^-6, True} *)
(* {2.4889, True} *)
Evaluation
The differences outlined above have implications for evaluation control. In particular, for functions which hold their argument, the "syntactic" patterns won't always work. For example:
ClearAll[a, f, g];
a = Range[10];
SetAttributes[{f,g}, HoldFirst];
f[l_List]:=l;
g[l_?ListQ]:=l;
{f[a], g[a]}
(* {f[a], {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}} *)
The point is, that even though a stores a List, f can't check that by only using the pattern-matcher. In contrast to this, g calls the main evaluator, which confirms that a evaluates to a List.
This effect has also another side: when you use a pattern like a_List in a Hold* - function, you can be sure that there will be no evaluation leaks, while in cases where you use the tests based on Condition or PatternTest, you have to make an extra effort to ensure that (if that is desired). For example:
ClearAll[a];
a := Print["Leak!"]
Cases[Unevaluated[{a, {a, {a}}}], s_List :> Hold[s], Infinity, Heads -> True]
(* {Hold[{a}], Hold[{a, {a}}]} *)
while
Cases[Unevaluated[{a, {a, {a}}}], s_?ListQ :> Hold[s], Infinity, Heads -> True]
During evaluation of In[296]:= Leak!
During evaluation of In[296]:= Leak!
During evaluation of In[296]:= Leak!
During evaluation of In[296]:= Leak!
During evaluation of In[296]:= Leak!
During evaluation of In[296]:= Leak!
(* Out[299]= {Hold[{a}], Hold[{a, {a}}]} *)
In this particular case, one would have to write someting like this, to avoid the leaks:
Cases[
Unevaluated[{a, {a, {a}}}],
s_?(Function[Null, ListQ[Unevaluated[#]], HoldAll]) :> Hold[s],
Infinity,
Heads -> True
]
(* {Hold[{a}], Hold[{a, {a}}]} *)
Pre-filtering
It is often useful to use the _h patterns even if they are not enough be themselves, for pre-filtering purposes.
Here is an example - first, a simple custom test for a list:
ClearAll[smallListQ];
smallListQ[x_ /; ListQ[x] && Length[x] < 20 && Total[x^2] < 1000] := True;
smallListQ[_] := False
Now, the same test, but with the part of a pattern using _h idiom:
ClearAll[smallListQBetter];
smallListQBetter[x_List /; Length[x] < 20 && Total[x^2] < 1000] := True;
smallListQBetter[_] := False
Compare performance:
smallListQ /@ Range[100000]; // AbsoluteTiming
(* {0.153377, Null} *)
smallListQBetter /@ Range[100000]; // AbsoluteTiming
(* {0.058185, Null} *)
In some cases, the difference may be far greater
When to use which
- Whenever you can get away with purely "syntactic" tests, surely du use them.
- Also, they can often be used when you defined a custom data type based on some head, used as a container for data. In those cases, the
_h test serves as a test for a given data type.
- Often you can't avoid using the
_?predicate or x_/;predicate[x] types of patterns, because the tests must call the main evaluator, for whatever reason. Just be aware of performance and evaluation control - related implications of this method
- Often one can combine both styles, using the
_h - style patterns as a pre-filtering device. This can improve both the robustness of the code (stronger typing), and the performance.
Related discussions
PatternTestandConditionfor defining a series ofDownValues? Both bring the evaluator into play, but I wonder if there is a reason to preferf[x_ /; test[x]]overf[x_?test]. – Jason B. Mar 31 '18 at 01:50Conditioncan be applied to a pattern involving several variables, likef[x_, y_] /; x < y, while withPatternTest, a similar thing would be more clunky. Another difference is that it is somewhat easier to control evaluation (leaks) withCondition, like e.g.Cases[Unevaluated[expr], s_Symbol /; Context[s] === "System`" :> Hold[s], Infinity], while forPatternTestone would have something likeFunction[sym, Context[sym] === "System`", HoldFirst], ... – Leonid Shifrin Mar 31 '18 at 14:32Functionsyntax withPatternTest- while inCondition, this problem does not exist, because there is no extra parameter-passing stage, where evaluation leak may happen. In terms of speed,PatternTesttends to be slightly faster, but if that difference starts to really be important, then may be pattern-matching is a wrong tool for that problem altogether. In terms of readability, I personally tend to usePatternTesta lot with predicates, likef[_?EvenQ], it is brief and clear. But this is largely a matter of taste. – Leonid Shifrin Mar 31 '18 at 14:36Contextdidn't have aHoldFirstattribute, I would have had to useUnevaluatedin both examples:s_Symbol /; someFunction[Unevaluated[s]]andFunction[sym, someFunction[Unevaluated @ sym], HoldFirst], which is what one generally needs to do to ensure safe passing of arguments to predicates without their premature evaluation (whenever that is important, which is in general not very often). Anyway, I personally tend to usePatternTestpretty much always, for a single-arg tests, andConditionfor multi-arg tests. – Leonid Shifrin Mar 31 '18 at 14:42Conditioncan be used in an extended rule / function definition syntax with so-called shared local variables, for examplex_Integer :> With[{result = x ^2}, f[result] /; result > 100], in which caseConditionis still considered a part of the pattern, even though it is used insideWith(can also beModuleorBlock), and operates on a local variable. If the condition is not fulfilled, the whole rule is considered not applicable by the patter-matcher, which goes on to try the next rule (if available), or returns an expression back, as if no evaluation ... – Leonid Shifrin Mar 31 '18 at 14:47result = x ^ 2happening in the process of resolving theCondition. This is a powerful feature associated specifically withConditionand notPatternTest. – Leonid Shifrin Mar 31 '18 at 15:19