5
$\begingroup$

I'm trying to get a list of all the variables in a symbolic expression. Some are named with Symbols, some are named with Subscript-ed symbols.

I can get a list of the Subscript-ed symbols with:

DeleteDuplicates@Cases[expr, Subscript[_Symbol, _Symbol], Infinity]

I can get a list of Symbols with:

DeleteDuplicates@Cases[expr, _Symbol, Infinity]

The problem with that second Cases statement is that it returns subscripted symbols in two parts-- the "base" and the "subscript". That is, Subscript[a,b] will return two symbols: {a,b}.

How do I construct a pattern such that I get back a list of all the symbolic variables of an expression, whether or not they're represented as subscripted? For example:

DeleteDuplicates@Cases[a+Subscript[b,c]*Subscript[d,e]^f,pattern,Infinity]

returns {a,Subscript[b,c],Subscript[d,e],f}?

$\endgroup$
2
  • $\begingroup$ What should it return on (for example) Subscript[f[t + t1], e + k]? $\endgroup$ Aug 10, 2015 at 4:45
  • $\begingroup$ @belisarius, I'm not so concerned with forms like that as I don't expect them in the input. The only place I have Subscript heads is when it's a subscripted variable such as Subscript[r,bat] where r and bat are unassigned symbols. $\endgroup$
    – Omegaman
    Aug 10, 2015 at 4:51

3 Answers 3

3
$\begingroup$

This is the very definition of a kluged-together solution (although I like the idea of replacement rules with side-effects), and it's not well-tested, but here's something that works for your example.

expr = a + Subscript[b, c]*Subscript[d, e]^f;

DeleteDuplicates@Flatten@Reap@Cases[
  expr /. Subscript[a_Symbol, b_Symbol] :> (Sow[Subscript[a, b]]; temp[])
  , _Symbol
  , Infinity
 ]
(* {a, f, Subscript[b, c], Subscript[d, e]} *)

The first replacement replaces the subscripts with the temp[] (which will not match a Symbol) while Sowing the Subscripted symbol. It then runs Cases to find the symbols, Reaps to get the Subscripts, Flattens so that all the symbols are together in one list, and finally runs DeleteDuplicates (Union works as well as used by belisarius).

Just as belisarius said about his answer, this is probably not the best solution, but matching a Symbol while not matching the Symbol when it has a particular Head is something I don't know how to do easily.

Update

We could also use the mysterious vanishing function:

DeleteDuplicates@Flatten@Reap@Cases[
  expr /. Subscript[a_Symbol, b_Symbol] :> (Sow[Subscript[a, b]]; ##&[])
  , _Symbol
  , Infinity
 ]
(* {a, f, Subscript[b, c], Subscript[d, e]} *)

The interesting bit here is

expr /. Subscript[a_Symbol, b_Symbol] :> (Sow[Subscript[a, b]]; ##&[])
(* a + f *)

The mysterious ##&[] removes all mention of the Subscripts, and f takes the place of that entire term.

$\endgroup$
4
  • 1
    $\begingroup$ I like this best as it appears to only make one pass through the expression-- I don't know if that's actually more efficient, but it feels it would be. I borrowed from @belisarius to replace your use of temp[] with Unique[][] because it feels less likely to step on a common function name. The vanishing function gives me something to study up on... $\endgroup$
    – Omegaman
    Aug 11, 2015 at 6:41
  • $\begingroup$ @Omegaman. That's why I like the vanishing function idea, as instead of replacing with some arbitrary expression, it removes the symbols completely (and actually it's not all that mysterious). I also have a standard Protected head temporaryPlaceHolder that I use for things like this so as to avoid clashing with already-defined names. Thanks for the accept! $\endgroup$
    – march
    Aug 11, 2015 at 14:14
  • $\begingroup$ …and in the latest version, the "vanishing function" is now called Nothing. $\endgroup$ Aug 14, 2015 at 2:30
  • $\begingroup$ Both solutions may lead to incorrect answers depending on the value of expr. For example, the one that uses temp[] will fail for expr = (Subscript[a, c] - Subscript[b, c])*f while the one that uses the vanishing function will fail for expr = (1 - Subscript[b, c])*f. Using Omegaman's comment and replacing temp[] by Unique[][] succeeds in both cases by preventing (Subscript[a, c] - Subscript[b, c]) from being replaced by zero and -Subscript[b, c] from being replaced by 1. $\endgroup$ Oct 4, 2017 at 19:47
2
$\begingroup$

Surely not the best way:

expr = a+Subscript[b,c]*Subscript[d,e]^f
pat = Subscript[_Symbol, _Symbol];
Union @@ {Cases[expr, pat, Infinity], 
          Cases[expr /. pat :> #, Except[#, _Symbol], Infinity] &@Unique[]}

(* {a, f, Subscript[b, c], Subscript[d, e]} *)
$\endgroup$
2
$\begingroup$

Depending on the complexity of the actual expression, this very simple solution may work better than Cases:

expr = a + Subscript[b, c]*Subscript[d, e]^f;

Variables[expr /. x : Except[_Plus | _Times | _Subscript] :> Times @@ x]

(* ==> {a, f, Subscript[b, c], Subscript[d, e]} *)

It uses the built-in function Variables which is designed to identify variables in polynomials. It can identify subscripted variables, too (as a side effect of it's ability to return anything that isn't simple enough to be decomposed as part of the variable list). To apply it here, all you have to do is convert the expression such that it contains only addition and multiplication operations (in addition to the atomic and subscripted parts). This is what the replacement rule does. It turns anything more complicated into Times, which in this case allows the exponent in Power to be counted as a variable, too. Then the variables are identified correctly.

$\endgroup$
1
  • $\begingroup$ +1 for a practical solution that works on my example expression, but the actual expressions I'm processing are not polynomial and seem to choke Variables. $\endgroup$
    – Omegaman
    Aug 11, 2015 at 6:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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