13
$\begingroup$

Mathematica provides an equivalent for "logical OR" when pattern matching, using Alternatives or the infix | operator. For instance, if I want to selected all lists and strings from a list, I could do that using:

Cases[input, _List | _String]

It appears that there is no equivalent way to combine several patterns with "logical AND", i.e. to require that all of the patterns match. As a simple example, say I have a list and I want to keep those lists that contain both a, b and b, c as sublists, but I don't mind what order those sublists are in, and they could even overlap on the b. Sure, I can do:

Cases[Cases[input, {___, a, b, ___}], {___, b, c, ___}]

or list all of the possibilities with |:

Cases[input, {___, a, b, c, ___} 
             | {___, a, b, ___, b, c, ___}
             | {___, b, c, ___, a, b, ___}]

However, the latter is absolutely infeasible, since the number of combinations will quickly blow up if there are more basis patterns and some combinations might not even be easily expressible as a single pattern. The former, is okay in this case, but having to call Cases twice seems annoying, and I'm sure there are other use cases of patterns, where I need to check for both patterns in a single pass.

One way to combine two patterns is to move one of them off to a PatternTest or Condition:

Cases[input, {___, a, b, ___} ? MatchQ[{___, b, c, ___}]]

Or as J. M. suggested in chat, just defer all of them to a condition and use the main pattern only as a dummy variable:

Cases[input, x_ /; MatchQ[x, {___, a, b, ___}] && MatchQ[x, {___, b, c, ___}]]

But none of these strike me as particularly nice solutions that follow "the Mathematica way".

Long story short, is there an idiomatic way to combine multiple patterns such that an object has to match all of those patterns? If not, am I overlooking better possibilities than the ones I've listed? Ideally there should be little syntactical and computational overhead for every additional pattern in the combination.

$\endgroup$
  • 4
    $\begingroup$ It is somewhat ugly, but you can use something like this: Cases[input, Except[Except[{___, a, b, ___}] | Except[{___, b, c, ___}]]] $\endgroup$ – Leonid Shifrin Apr 6 '16 at 12:31
  • 3
    $\begingroup$ @N.J.Evans That's very specific to the Cases application. Ideally I want a solution that I can insert anywhere Mathematica expects a single pattern. $\endgroup$ – Martin Ender Apr 6 '16 at 15:06
  • 4
    $\begingroup$ Sure, that's trivial to do: AndPattern[patts___] := Except[Alternatives @@ Map[Except, {patts}]]. Then it looks more pleasant: Cases[input, AndPattern[{___, a, b, ___}, {___, b, c, ___}]]. I don't know a better way for this problem. OTOH, frankly, I never needed it. Whether it really means that such cases are rare in practice, or is a Sapir-Whorf phenomena at work, I can't say. $\endgroup$ – Leonid Shifrin Apr 6 '16 at 15:27
  • 1
    $\begingroup$ By using OrderlessPatternSequence[PatternSequence[...], PatternSequence[..]] and by defining additional patterns which catch potential overlaps you might reduce the number of patterns you have to write. It will be like And if there are no overlaps. Just mentioning it for completeness' sake. $\endgroup$ – C. E. Apr 6 '16 at 15:54
  • 1
    $\begingroup$ @LeonidShifrin "There is no established idiom, but here is a way to write a simple wrapper without major drawbacks." would be a valid answer. ;) $\endgroup$ – Martin Ender Apr 6 '16 at 15:55