I want to provide a function f[x_,y___] which applies a pattern matching test also on the optional parameters (e.g. request that they are vectors of numerical quantities).
In case of one single argument one would write:
Clear[f];
f[x_ /; VectorQ[x, NumericQ]] := "This one matches " ~~ ToString[x];
f[{1, 2., Pi}]
gives me "This one matches {1, 2., Pi}"
and f[True]
returns unevaluated, as expected.
Of course, for two arguments I could add another definition
f[x_ /; VectorQ[x, NumericQ]
,y_ /; VectorQ[y, NumericQ]
] := "This one matches 1st="~~ToString[x]~~" 2nd="~~ToString[y];
for which
f[{1, 2., Pi}]
f[{1, 2., Pi}, {3, 2.0, 1}]
returns
"This one matches {1, 2., Pi}"
"This one matches 1st={1, 2., Pi} 2nd={3, 2., 1}"
and of course it does not match f[True] or f[True, False] but returns them unevaluated, as designed.
Now I want to extend this to an arbitrary number (including 0) of additional arguments. Before I do that, I want to check the sequence of events during parameter testing and therefore I try it with a single new defintion and Clear the old one first:
Clear[f];
f[x_ /; (Print["Test x:", x]; True)
,y___ /; (Print["Test y:", y]; True)
] := "This one matches 1st=" ~~ ToString[x]~~" rest="~~ToString[Hold[y]];
This one does not really test the arguments but it shows, what is considered during testing:
f[{1}]
f[{1}, {2}]
f[{1}, {2}, {3}]
yields
During evaluation of In[133]:= Test x:{1}
During evaluation of In[133]:= Test y:
Out[133]= "This one matches 1st={1} rest=Hold[]"
During evaluation of In[133]:= Test x:{1}
During evaluation of In[133]:= Test y:{2}
Out[134]= "This one matches 1st={1} rest=Hold[{2}]"
During evaluation of In[133]:= Test x:{1}
During evaluation of In[133]:= Test y:{2}{3}
Out[135]= "This one matches 1st={1} rest=Hold[{2}, {3}]"
This shows us:
- Test x is always applied to the first parameter
- Test y is always applied to all subsequent parameters simultaneously
This in fact explains, why my first attempt failed, when I tried it like this:
Clear[f];
f[x_ /; VectorQ[x, NumericQ]
,y___ /; VectorQ[y, NumericQ]
] := "This one matches 1st="~~ToString[x]~~" rest="~~ToString[Hold[y]];
Let me test it:
f[{1, 2., Pi}]
f[{1, 2., Pi}, {2, 2., Pi}]
f[{1, 2., Pi}, {2, 2., Pi}, {3, 3., Pi}]
Out[96]= f[{1, 2., \[Pi]}]
Out[97]= "This one matches 1st={1, 2., Pi} rest=Hold[{2, 2., Pi}]"
During evaluation of In[96]:= VectorQ::argt: VectorQ called with 3 arguments; 1 or 2 arguments are expected. >>
Out[98]= f[{1, 2., \[Pi]}, {2, 2., \[Pi]}, {3, 3., \[Pi]}]
The second "provided that" parameter test for the zero or more subsequent parameters is not applied to each actual parameter separately but it is applied to them simultaneously. Then of course VectorQ has to complain on the wrong number or type of parameters if it is called with 3 or more parameters.
How would one formulate such a function which can receive an arbitrary number of lists, but VectorQ[#, NumericQ]& must apply for each of them?
How would one internally determine the actual number of such parameters and how would one address them individually in further computations - if the pattern matching test in the definition has applied?
Or would you recommend to avoid pattern matching on the function definition level and rather distinguish different cases in the body of my function as described here, when it is about testing for more than just the structure of actual parameters with NumericQ, ListQ, StringQ and the like?
PatternTest
: "In a form such as__?test
, every element in the sequence matched by__
must yieldTrue
whentest
is applied." So I would use that:f[x_?(VectorQ[#, NumericQ] &), y___?(VectorQ[#, NumericQ] &)] := "This one matches 1st=" ~~ ToString[x] ~~ " rest=" ~~ ToString[Hold[y]];
This works withf[{1, 2., Pi}, {2, 2., Pi}, {3, 3., Pi}]
at least. Have I understood your question correctly? – Marius Ladegård Meyer Oct 29 at 14:25