25
$\begingroup$

I'm looking for a way to extract a list of variables from an expression, for example with an input like:

Leff= (mc dm^2 + mc/12*(h^2 + 3 R^2) + ma da^2 + ma/12 La^2)/(mc dm + ma da)

I want this output:

{mc, dm, ma, da, La, h, R}.

The built-in Mathematica function Variables can do this, but it doesn't work with more complex expressions containing trascendental functions. Any help would be very appreciated.

$\endgroup$
3
  • $\begingroup$ Wouldn't it be more natural to use { c, d, a, b, L, h, R} instead of unreasonable and awful {mc, dm, ma, da, La, h, R} ? $\endgroup$
    – Artes
    Mar 13 '13 at 20:24
  • 5
    $\begingroup$ @Artes Mnemonics can often be helpful, as well as using variables whose names are as close as possible to a formula as printed in a textbook or paper. Let's blame the paper authors and not the coders for bad variable names! $\endgroup$
    – whuber
    Mar 13 '13 at 20:41
  • $\begingroup$ Related: (15153) $\endgroup$
    – Mr.Wizard
    Mar 13 '13 at 23:24
28
$\begingroup$

Assuming you don't have any built-in symbols in that list, you could simply do:

DeleteDuplicates@Cases[Leff, _Symbol, Infinity]
(* {da, ma, dm, mc, La, h, R} *)

If you do have symbols from built-in contexts or packages, you can simply pick out only those that are in the Global` context with:

With[{globalQ = Context@# === "Global`" &},
    DeleteDuplicates@Cases[Leff, _Symbol?globalQ, Infinity]
]

If you have a different default working context (e.g. local to notebook/cell or in a package), change the pattern test to the following, instead of globalQ:

currentContextQ = Context@# === $Context &
$\endgroup$
1
  • 1
    $\begingroup$ Thank you very much, this is exactly what I was looking for. $\endgroup$
    – John
    Mar 13 '13 at 20:32
16
$\begingroup$

Using an undocumented function:

Reduce`FreeVariables[(mc dm^2 + mc/12*(h^2 + 3 R^2) + ma da^2 + ma/12 La^2)/
                     (mc dm + ma da)]
   {da, dm, h, La, ma, mc, R}
$\endgroup$
10
$\begingroup$

The below code for getAllVariables was lifted without attribution from some StackOverflow post.

headlist = {Or, And, Equal, Unequal, Less, LessEqual, Greater, 
   GreaterEqual, Inequality};

getAllVariables[f_?NumericQ] := Sequence[]
getAllVariables[{}] := Sequence[]
getAllVariables[t_] /; MemberQ[headlist, t] := Sequence[]

getAllVariables[ll_List] := 
 Flatten[Union[Map[getAllVariables[#] &, ll]]]

getAllVariables[Derivative[n_Integer][f_][arg__]] := 
 getAllVariables[{arg}]

getAllVariables[f_Symbol[arg__]] := 
 Module[{fvars}, 
  If[MemberQ[Attributes[f], NumericFunction] || MemberQ[headlist, f], 
   fvars = getAllVariables[{arg}],(*else*)fvars = f[arg]];
  fvars]

getAllVariables[other_] := other

Example:

Leff = (mc dm^2 + mc/12*(h^2 + 3 R^2) + ma da^2 + ma/12 La^2)/(mc dm +
     ma da)
getAllVariables[Leff]

(* Out[254]= {da, ma, dm, mc, ma, da, ma, La, mc, dm, mc, h, R} *)
$\endgroup$
2
  • $\begingroup$ Daniel, since questions are generally not being migrated any longer and since these valuable posts ultimately fall outside our control, why don't you repost your method as a full answer? $\endgroup$
    – Mr.Wizard
    Mar 16 '13 at 14:15
  • 3
    $\begingroup$ @Mr.Wizard Done. (But if you were a real wizard, couldn't you just make it magically happen?) $\endgroup$ Mar 16 '13 at 20:44
7
$\begingroup$

If you have even more complicated expressions, you might want to use Heads -> True.

expr = {f, Subscript[g, i], h[i[j[a, b]]], s'[t] == u[t] + v[t]};

Union @ Cases[expr, Except[__Symbol?(Context @ # === "System`" &), _Symbol], {1, ∞},
              Heads -> True]
{a, b, f, g, h, i, j, s, t, u, v}

Without checking heads:

Union @ Cases[expr, Except[__Symbol?(Context @ # === "System`" &), _Symbol], {1, ∞}]
{a, b, f, g, i, t}
$\endgroup$
3
  • 2
    $\begingroup$ This is the method I would use, expect perhaps as Union @ Cases[Leff, s_Symbol /; Context[s] =!= "System`", -1] $\endgroup$
    – Mr.Wizard
    Mar 13 '13 at 23:11
  • $\begingroup$ @Mr.Wizard I've tried to provide code that is sufficiently different from rm-rf-s version not to give room for any comments on plagiarism :) $\endgroup$ Mar 14 '13 at 7:46
  • $\begingroup$ Thank you, the expressions I usually work with are not so complicated but I might need this sometimes. $\endgroup$
    – John
    Mar 14 '13 at 15:19
2
$\begingroup$
expr = (mc dm^2 + mc/12*(h^2 + 3 R^2) + ma da^2 + ma/12 La^2)/(mc dm + ma da); 
Level[expr, {-1}] /. x_?NumericQ :> (## &[]) // DeleteDuplicates
(* {da,ma,dm,mc,La,h,R} *)
$\endgroup$

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.