# 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 '14 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 '14 at 19:04
• Abbreviation will give a better result than before, thanks @JohnMorganthau! Dec 11 '14 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 '18 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 '14 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 '14 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 '14 at 19:07
• Certainly the @Szabolcs solution is best than my initial approach. Thanks to everybody! Dec 11 '14 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 '14 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 *)