1
$\begingroup$

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?

$\endgroup$
  • $\begingroup$ From the docs of PatternTest: "In a form such as __?test, every element in the sequence matched by __ must yield True when test 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 with f[{1, 2., Pi}, {2, 2., Pi}, {3, 3., Pi}] at least. Have I understood your question correctly? $\endgroup$ – Marius Ladegård Meyer Oct 29 '16 at 14:25
  • $\begingroup$ The point is not to refer to the parameter names (e.g. y) as I did but to use a pure function as you did, if the parameter is part of a BlankNullSequence. So Marius' solution is perfect. - But one question remains: Why did f[{1},{2},{3}] return me a single line “During evaluation of In[133]:= Test y:{2}{3}”? With what you found in the documentation of PatternTest, I would have expected in total three lines of which two are identical, one them from testing the argument {2} and the other from testing the argument {3} and both times Sequence[{2},{3}] being in y: Therefore two identical lines. $\endgroup$ – Adalbert Hanßen Oct 30 '16 at 18:53

Your Answer

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

Browse other questions tagged or ask your own question.