# How to avoid repeated pattern tests in function definitions

When defining some functions which depend on many arguments, sometimes we need to include predicate constraints (?xxxQ) to reduce processing time. My question is simple: is there a way to shorten a long function definition like this:

f[x_?NumericQ,y_?NumericQ,z_?NumericQ,k_?IntegerQ,l_?IntegerQ]:=Stuff[x,y,z,k,l]


to produce cleaner code?

• I abbreviate the type names like num = NumericQ. Shorter but not necessarily easier to follow for other users. Dec 11, 2014 at 19:00
• Not ideal, but if they are all scalar you can "listify" the arguments. Something like this: f[numlist : {_?NumericQ ..}] := Module[{x, y, z, k, l}, {x, y, z, k, l} = numlist; ...]. Dec 11, 2014 at 19:04
• Abbreviation will give a better result than before, thanks @JohnMorganthau! Dec 11, 2014 at 19:05

You can use PatternSequence:

f[PatternSequence[x_,y_,z_]?NumericQ, PatternSequence[k_,l_]?IntegerQ] := stuff[x,y,z,k,l]


Check:

f[1,2,3,π,5]
f[π,1,2,3,4]


f[1, 2, 3, π, 5]

stuff[π, 1, 2, 3, 4]

• The idea to combine PatternSequence with PatternTest would've never occurred to me. Nov 1, 2018 at 10:18

Perhaps something like this:

allNumeric[vars_] := VectorQ[{vars}, NumericQ] (* define once, use many times *)

f[x_, y_, z_, t_] /; allNumeric[x,y,z,t] := ...

• Just in case you didn't see it, the OP has a mixture of heads there. Dec 11, 2014 at 19:03
• @belisarius Yes, I was lazy, and hoped to imply that one can do /; allNumeric[...] && allInteger[...] .... Feel free to post something better and I'll delete. Dec 11, 2014 at 19:04
• Nah, it was just in case you didn't notice. I think it must be pretty clear now for him. Dec 11, 2014 at 19:07
• Certainly the @Szabolcs solution is best than my initial approach. Thanks to everybody! Dec 11, 2014 at 19:21
• @robsondenke At one point I just made a "Personal " package where I collect little functions that I use often. I try to make an effort to detect repeating patterns in my work and package them up into small utility functions. In the long term this turned out to be a time saver (even though I have only a few functions in the package). The disadvantage is that it becomes harder to share your code with others (you'd need to give them these utility functions as well). Dec 11, 2014 at 19:22

One way may be to define global shorthand notations for pattern matching.

For example, I get the result

f[a_?i]:=2 a;
f//Information
(* Globalf *)
(* f[a_?IntegerQ]:=2 a *)


with the code

ClearAll[shortHand, shortHandReplace, shortHandReplacements, patternTest];

shortHand = {{i, IntegerQ}, {nric, NumericQ}, {num, NumberQ}};

shortHandReplacements = Block[{temp}, Table[patternTest[a_, term[[1]]]:> patternTest[a, temp] /. temp -> term[[2]], {term,shortHand}]];

SetAttributes[shortHandReplace, HoldAllComplete]
shortHandReplace[input_] := ReleaseHold[HoldForm[input] /. PatternTest-> patternTest /. shortHandReplacements /. patternTest ->PatternTest];

$Pre = Function[input, shortHandReplace[input], HoldAllComplete];  Basically, shorthand is a list such that any encounter of the first item in a PatternTest is to be replaced with second item before evaluation. For example, {i,IntegerQ} in the list ensures that even though we type f[a_?i], it is evaluated as f[a_?IntegerQ]. The function shortHandReplacements create these replacements, and shortHandReplace apply those to given input. I use $Pre so that this is applied to all input.

This shorthand works in the rules as well because it only relies on the presence of PatternTest. For example,

g[3]/.a_?i:>2 a
(* g[6] *)


One advantage of the code is that the shorthand notation does not affect the code outside so using these shortcuts is no-means a limitation on these variables:

ClearAll[f];
f[i_?i]:=Cos[i]
?f
(* Globalf *)
(* f[i_?IntegerQ]:=Cos[i] *)


Another advantage of the code is that we can arbitrarily define tests and shorthand notations for them easily. For example:

ClearAll[divisibleByThreeQ,divisibleBySevenQ];
divisibleByThreeQ=(Divisible[#,3]&);
divisibleBySevenQ=(Divisible[#,7]&);
shortHand={{i,IntegerQ},{nric,NumericQ},{num,NumberQ},{d3,divisibleBySevenQ},{d7,divisibleBySevenQ}};


allows us to define

ClearAll[j];
j[a_?d3,b_?d7]:=a+b;
?j
(* Globalj *)
(* j[a_?(Divisible[#1,3]&),b_?(Divisible[#1,7]&)]:=a+b *)