5
$\begingroup$

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?

$\endgroup$
  • 1
    $\begingroup$ I abbreviate the type names like num = NumericQ. Shorter but not necessarily easier to follow for other users. $\endgroup$ – John Morganthau Dec 11 '14 at 19:00
  • 3
    $\begingroup$ 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; ...]. $\endgroup$ – Daniel Lichtblau Dec 11 '14 at 19:04
  • $\begingroup$ Abbreviation will give a better result than before, thanks @JohnMorganthau! $\endgroup$ – robson denke Dec 11 '14 at 19:05
10
$\begingroup$

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]

$\endgroup$
  • $\begingroup$ The idea to combine PatternSequence with PatternTest would've never occurred to me. $\endgroup$ – Sjoerd Smit Nov 1 '18 at 10:18
7
$\begingroup$

Perhaps something like this:

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

f[x_, y_, z_, t_] /; allNumeric[x,y,z,t] := ...
$\endgroup$
  • 1
    $\begingroup$ Just in case you didn't see it, the OP has a mixture of heads there. $\endgroup$ – Dr. belisarius Dec 11 '14 at 19:03
  • $\begingroup$ @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. $\endgroup$ – Szabolcs Dec 11 '14 at 19:04
  • $\begingroup$ Nah, it was just in case you didn't notice. I think it must be pretty clear now for him. $\endgroup$ – Dr. belisarius Dec 11 '14 at 19:07
  • $\begingroup$ Certainly the @Szabolcs solution is best than my initial approach. Thanks to everybody! $\endgroup$ – robson denke Dec 11 '14 at 19:21
  • $\begingroup$ @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). $\endgroup$ – Szabolcs Dec 11 '14 at 19:22
1
$\begingroup$

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 *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.