23
$\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$
  • $\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
26
$\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$
  • $\begingroup$ Thank you very much, this is exactly what I was looking for. $\endgroup$ – John Mar 13 '13 at 20:32
15
$\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$
  • $\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
  • 2
    $\begingroup$ @Mr.Wizard Done. (But if you were a real wizard, couldn't you just make it magically happen?) $\endgroup$ – Daniel Lichtblau 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$
  • 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$ – István Zachar 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.