In preparing a response to An unexpected behavior of PatternTest in Mathematica I came across an unexpected Mathematica behavior of my own.
Please consider:
test = (Print[##]; False) &;
MatchQ[{1, 2, 3, 4, 5}, {x__?test, y__}]
During evaluation of In[1]:= 1 During evaluation of In[1]:= 1 During evaluation of In[1]:= 1 During evaluation of In[1]:= 1
False
Since, as Simon's quote of the documentation concisely states:
In a form such as
__?test
every element in the sequence matched by__
must yieldTrue
when test is applied.
I am wondering why Mathematica is testing the first element of the list four separate times. Of course there are four ways to make up the underlying pattern {x__, y__}
and if this were a Condition
test then all elements that make up the sequence x
would need to be tested, but I don't think that is the case here.
Does the logic not hold that if the first element of the list fails PatternTest
then given pattern cannot match?
If it does hold, why is Mathematica not making this simple optimization?
Borrowing an example from yoda's answer, here is another example of what appears to be excess evaluation:
In[1]:= test2 = (Print@##; Positive@##) &;
MatchQ[{1, 2, 3, 4, -5}, {x__?test2, y__?Negative}]
During evaluation of In[1]:= 1
During evaluation of In[1]:= 1
During evaluation of In[1]:= 2
During evaluation of In[1]:= 1
During evaluation of In[1]:= 2
During evaluation of In[1]:= 3
During evaluation of In[1]:= 1
During evaluation of In[1]:= 2
During evaluation of In[1]:= 3
During evaluation of In[1]:= 4
Out[2]= True
I admit to having never explored this aspect of pattern matching before, and I am disturbed by the seeming inefficiency of this. Is this really as bad as it looks, or is some kind of automatic caching taking place? Print
appears to indicate against that.
Is there a more efficient pattern based way to write this?
Is this level of redundancy required for correct pattern matching behavior, and why?
I made a false assertion in haste, but I leave it because good answers below address it. Please ignore it in future answers.
It is easy to demonstrate that a similar optimization is made in other cases:
MatchQ[{-1, 2, 3, 4, 5}, {__?Positive, y__?test}]
(Nothing is printed.)
FalseHere Mathematica correctly never even tests any elements for
y
.