7

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?

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
robson denke
  • 984
  • 6
  • 13

3 Answers3

12

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]

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
8

Perhaps something like this:

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

f[x_, y_, z_, t_] /; allNumeric[x,y,z,t] := ...
Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • 1
    Just in case you didn't see it, the OP has a mixture of heads there. – Dr. belisarius 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. – Szabolcs 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. – Dr. belisarius Dec 11 '14 at 19:07
  • Certainly the @Szabolcs solution is best than my initial approach. Thanks to everybody! – robson denke 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). – Szabolcs Dec 11 '14 at 19:22
2

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
(* Global`f *)
(* 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
(* Global`f *)
(* 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
(* Global`j *)
(* j[a_?(Divisible[#1,3]&),b_?(Divisible[#1,7]&)]:=a+b *)
SonerAlbayrak
  • 2,088
  • 10
  • 11