How to match set-patterns against sets?
A set (in the mathematical sense) is a list of elements without repetition and order of elements does not matter. For example, we have a pattern set {3, 1}
that should match sets {1, 3}
, {1, 2, 3}
, {1, 2, 3, 4}
and so on. Note, that the list-length of the pattern is not relevant: any set that contains elements 3 and 1 should match the pattern. So far, this is simple subset testing - but there are two problems:
Since order does matter for the patternmatcher, one has to write e.g.
Cases[sets, {___, 3, ___, 1, ____}|{___, 1, ___, 3, ____}]
which causes a combinatorial expansion for an increasing number of element-wise matches. Thus I usedMemberQ
instead of structural patterns.I would like to use more complicated patterns, like: "Find all sets that contain 1 and 3 but not 2!".
I have a working solution, but it is neither effective nor elegant in my opinion. It involves a Boolean description of the pattern (And
to include all listed elements, Or
to include any listed element, Not
to exclude an element), but I am not sure it is the right way to do it. The function simply wraps each element that apperas in the pattern into MemberQ
, so the Boolean expression translates to a logical combination of MemberQ
and Not@MemberQ
calls.
setCases[sets_List, patt_] := Module[{elem = Union @@ sets},
Cases[sets, _?((patt /. x_ /; MemberQ[elem, x] :> MemberQ[#, x]) &), {1}]
];
Define a list of sets, and a list of patterns for testing:
sets = Subsets[{1, 2, 3, 4}];
patterns = {1, \[Not] 1, 1 \[And] \[Not] 2, 1 \[Or] \[Not] 2,
1 \[Or] 2 \[Or] 3, 1 \[And] 2 \[And] 3,
1 \[And] \[Not] 2 \[And] \[Not] 3, 1 \[Or] (2 \[And] \[Not] 3),
1 \[And] \[Not] (2 \[And] 3), 1 \[And] \[Not] (2 \[Or] 3),
1 \[And] \[Not] (2 \[Or] (3 \[And] \[Not] 4)),
\[Not] 1 \[And] \[Not] 2 \[And] \[Not] 3 \[And] \[Not] 4}
Grid[{#, setCases[sets, #]} & /@ patterns, Alignment -> Left,
Background -> {None, {{LightGray, White}}}, Spacings -> {1, 1}] // TraditionalForm
Let's examine one case closer, by displaying the ultimate pattern that is tested:
(1 \[Or] 2) \[And] \[Not] 3 /. x_Integer :> MemberQ[#, x]
(MemberQ[#1, 1] & || (MemberQ[#1, 2] &)) && ! (MemberQ[#1, 3] &)
As one can see, the function is far from being economic: alternatives could have been gathered under one MemberQ
(MemberQ[#, 1]& || MemberQ[#, 2]&
is equivalent to MemberQ[#, 1|2]&
) and I think that Except
should be used as well, though have no idea how. I am interested in robust, fast solutions.
Note: Do NOT try to simplify the logical patterns, as:
Simplify[And[1, 2]] ==> 2
Simplify[And[0, 1]] ==> False
TextSearch
. It usesList
forAnd
,Alternatives
forOr
andExcept
forNot
. According toPrintDefinitions
spelunking, there is a full-blown query-set-algebra, check e.g.TextSearch^IndexSearch^PackagePrivate^compileQuery
and...^exec
:QNot
,QUnion
,QIntersection
,QComplement
,QString
are the specific functions. (backticks are replaced by^
) $\endgroup$