Quick way to use conditioned patterns when defining multi-argument function?
Ramblings
Arguments of the left-hand-side head are evaluated in the course of function definition, therefore you can use a utility function that constructs the patterns that you want. For example:
SetAttributes[nq, HoldFirst]
Quiet[
nq[s_Symbol] := s_?NumericQ
]
Now:
ClearAll[f]
f[nq @ a, nq @ b, nq @ c] := a + b + c
Definition[f]
f[a_?NumericQ, b_?NumericQ, c_?NumericQ] := a + b + c
Doing this you lose the nice syntax highlighting shown in the original.
If you Block
all Symbols you could even Map
the utility function, e.g.:
Block[{f, a, b, c},
Evaluate[nq /@ f[a, b, c]] := a + b + c
]
This hardly feels like a clean solution however. Perhaps you merely want something shorter than verbatim NumericQ
? At risk of a tautology you could always do something like:
ClearAll[f]
q = NumericQ;
f[a_?q, b_?q, c_?q] := a + b + c
But this requires you to keep the Global definition q
or it will break as it is not expanded to NumericQ
in the definition itself:
Definition[f]
f[a_?q, b_?q, c_?q] := a + b + c
Metaprogramming approach
Another approach would be to write a function to modify all Pattern
objects on the left-hand-side at the time of assignment. Something like:
SetAttributes[defWithTest, HoldFirst]
defWithTest[(s : Set | SetDelayed)[LHS_, RHS_], test_] :=
s @@ Join[Hold[LHS] /. p_Pattern :> p?test, Hold[RHS]]
Now:
ClearAll[f]
defWithTest[
f[a_, b_, c_] := a + b + c,
NumericQ
]
Definition[f]
f[a_?NumericQ, b_?NumericQ, c_?NumericQ] := a + b + c
Proposed solution
As Kuba and rasher show in the comments you could also use clever alternatives to the explicit form f[a_?NumericQ, b_?NumericQ, c_?NumericQ]
. Inspired by those comments I propose:
SetAttributes[numArgsQ, HoldFirst]
numArgsQ[_[___?NumericQ]] := True
Now:
ClearAll[f]
f[a_, b_, c_]?numArgsQ := a + b + c
Test:
f[1, 2, 3]
f["a", 2, 3]
6 f["a", 2, 3]
For examples of advanced argument testing with an emphasis on messages please see:
- How to check the style and number of arguments like the built-in functions?
Note how (some) internal functions pass additional argument checking (and message generation) to an auxiliary function, e.g. ChartArgCheck
, much as I did in the minimal application of numArgsQ
above.
Update
I was reading the comments to the question, and found that @Kuba had already provided the following answer. I think it's the cleanest solution, so it deserves to be an answer, but please credit him with the idea.
Another idea is to use PatternSequence
:
ClearAll[f]
f[PatternSequence[a_,b_,c_]?NumericQ] := a+b+c
Examples:
f[1,2,3]
f["a",2,3]
6
f["a", 2, 3]
Generalization
We can generalize the idea behind this answer as follows:
SequencedPattern[a_] := PatternSequence[##]?a&
Then, we can use SequencedPattern
as follows:
f[SequencedPattern[NumericQ][a_, b_, c_]] := a + b + c
The previous examples still work:
f[1, 2, 3]
f["a", 2, 3]
6
f["a", 2, 3]
Here is another example using SequencedPattern
:
f[SequencedPattern[PrimeQ][a_, b_, c_], d_] := (a+b+c)/d
f[2,3,4,10]
f[2,3,7,11]
f[2, 3, 4, 10]
12/11
I think there is another way to do that:
f[s : (PatternSequence[_?NumericQ] ..)] /; Length[{s}] == 3 :=
Function[{a, b, c}, a^2 Sin[b] Log[c]][s]
It's suitable for the cases that conditions are the same.