9
$\begingroup$

Seemingly a relatively simple problem, I can't seem to find a solution...

I want to build a pattern that will match any symbol whose name starts with V, and is elevated at any power. Something on the line of:

MatchQ[#1, (V1 | V2 | V3)^_.] & /@ {V1, V1^2, V2}

That returns true for each of the entries. I haven't found a way to put constrains on the symbol name, if not by using SymbolName[] and working with string patterns:

MatchQ[#1, (_)?(StringMatchQ[SymbolName[#1], "V" ~~ __] &)^_.] & /@ {V1, V1^2, V2}

However, it doesn't look exactly beautiful... (only to me?) :-) Is there a more compact and elegant way of putting constraints on a symbol name? I'm feeling like I'm totally missing a very obvious function that would do that...

$\endgroup$

2 Answers 2

11
$\begingroup$

I believe that if you want to match a Symbol by its constituent characters that you do need to use string patterns, therefore your solution is reasonable. I recommend a variation:

p2 = s_Symbol^_. /; StringMatchQ[SymbolName[s], "V*"]

s_Symbol makes sure that the expression that is passed to SymbolName is actually a Symbol. I also find it somewhat more readable. Test:

MatchQ[p2] /@ {V1, V1^2, V2, x^2, y}   (* v10 operator form syntax *)
{True, True, True, False, False}

The use of Condition rather than PatternTest makes it easier if you may be working with held expressions and need to add Unevaluated:

p3 = s_Symbol^_. /; StringMatchQ[SymbolName[Unevaluated@s], "V*"]

This allows:

V1 = "Fail!";
Cases[Hold[V1, V1^2, V2, x^2, y], x : p2 :> Hold[x]]
V1 =.;
{Hold[V1], Hold[V1^2], Hold[V2]}

To make the code a bit prettier consider an abstraction such as:

symPat = s_Symbol /; StringMatchQ[SymbolName[Unevaluated@s], #] &;

Then simply:

Cases[{V1, V1^2, V2, x^2, y}, symPat["V*"]^_.]
{V1, V1^2, V2}
$\endgroup$
2
  • $\begingroup$ Thank you, not only for he answer to the point, but for the additional info as well. $\endgroup$ Commented Feb 18, 2015 at 22:45
  • $\begingroup$ Just for the sake of sharing, what drew me off was the (semi-)unconscious and naive belief that by writing 'MatcQ[V1, V1]' (that I never really wrote) I was comparing the names of the symbols. Thus my reasoning was: if I can compare entire names, and "any" by using _ (sort of * for symbol names), then there musts be a syntax to compare only part of the name (eventually completed by wildcards). Now I realize that 'MatchQ' compares the content of the symbols, hence the need to use SymbolName if you want to put constraints on the name... probably this was very clear to everyone except me! :-) $\endgroup$ Commented Feb 19, 2015 at 3:34
1
$\begingroup$

Thanks to Mr. Wizard for the excellent answer above. I just came across this post in search for an answer to a similar but not-quite-identical problem.

FWIW, here is my take on the answer posted. My main use-case for it is filtering rules in long sets of rules, e.g. AbsoluteOptions[EvaluationCell[]:

StringRepresentationContainsQ[searchFor_String, 
  ignoreCase : _?(BooleanQ) : True] :=
 Function[searchIn,
  StringContainsQ[searchFor, IgnoreCase -> ignoreCase]@
   Replace[{s_Symbol :> SymbolName@s, 
      e : Except[_String] :> ToString[e]}]@HoldForm@searchIn, 
  HoldAllComplete]

Example usage:

MarginQ = StringRepresentationContainsQ["margin"]
FilterRules[AbsoluteOptions[EvaluationCell[]], _?(MarginQ)]

Results:

{CellFrameLabelMargins -> 6, CellFrameMargins -> 8, 
 CellLabelMargins -> {{12, 0}, {2, 0}}, 
 CellMargins -> {{66, 10}, {5, 8}}, ImageMargins -> 0, 
 WindowMargins -> {{84, 548}, {16, 84}}}

For the OP's original post, just replace StringContainsQ with StringMatchQ or StringStartsQ...

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.