Third times's the charm, isn't it? ;)
Similar in spirit to my other answer using GeneralUtilities`PatternSort
, but fully documented and more robust:
robustRuleSort[rules_] := Module[
{ruleSorter},
(ruleSorter[# _.]dummyPat_] := {{#2}}) & @@@ rules;
DownValues[ruleSorter][[All, All, 1, 1]]
/. p_ Verbatim[dummyPat_] :> p
]
n2/n3/n4/n5 + n1*n2/n3/n4/n5 /. robustRuleSort@{n2/n3/n4/n5 -> a2, n1*n2/n3/n4/n5 -> e}
(* 2 a2 + e *)
###How it works
The basic idea is to (ab)use the rule ordering mechanism of downvalues to order the rules for us. This has the advantage that we use the best available specificity tester available, rather that something that tries to emulate it (like GeneralUtilities`PatternSort
or Internal`ComparePatterns
). This requires a few things:
- A dummy symbol to attach our rules to (
ruleSorter
). We will attach our rules as downvalues, which are then ordered by specificity (see here). - We need to make sure the rules are not literal downvalues (ordering doesn't work for them). Hence the
_dummyPat
factor we add. We later remove it usingReplaceAll
(/.
that should always be matched by nothing) (note the use ofVerbatim
) - The right-hand-side of the downvalues is wrapped in
{{…}}
to make extraction easier:DownValues[sym]
are always of the form{HoldPattern[sym[lhs]]:>rhs}
. This means we need to strip off two layers on the lhs, so we add two on the rhs to even it out.
This results in a rule sorter that should be as good as possible