I would like to test whether an argument is a valid variable for functions like Solve and DSolve. For instance several functions generate a "dsvar" or "ivar" message on bad input:
DSolve::dsvar: 2 x cannot be used as a variable. >>
Integrate::ivar: Sin[x] is not a valid variable. >>
I would like to check an argument before passing it onto one of these functions.
I used the function withBlockedVars in this answer to come up with what seems like a maybe-not-very-bad way. I block the variable and attempt to assign a value to it.
ClearAll[withBlockedVars];
SetAttributes[withBlockedVars, HoldRest];
withBlockedVars[Hold[expr_], code_] :=
With[{heldVars =
Thread[Cases[Unevaluated[expr],
s_Symbol /; Context[s] === "Global`" && DownValues[s] === {} :> HoldComplete[s],
Infinity,
Heads -> True],
HoldComplete]},
heldVars /. HoldComplete[vars_List] :> Block[vars, code]]
SetAttributes[variableQ, HoldAll];
variableQ[x_] := withBlockedVars[Hold[x], Quiet@Check[x = 0; True, False]];
Tests:
t = 2;
variableQ[2^t]
(* False *)
t = 2; x = 3;
variableQ[x[2^t]]
(* True *)
variableQ[t[2]]
(* True *)
variableQ[Subscript[t, 2]]
(* True *)
My use-case is for defining functions, something like this:
f[eqn_Equal, var_?variableQ] := code
The code might call NDSolve or Plot and so on.
Is there a better way to check var? Perhaps there is a built-in function I missed?
[Edit: Any solution involving Pattern, PatternTest, Condition etc. would be acceptable. I'm not sure I can think of all the alternative possibilities.]
variableQor are you OK with alternate approaches? – rm -rf Jan 10 '14 at 21:26Blockalone will miss some cases, e.g.Protectedsymbols. – Leonid Shifrin Jan 10 '14 at 21:32variableQ[var_] := Quiet[Check[Solve[0, var]~Quiet~Solve::naqs; True, False, Solve::ivar], Solve::ivar]– Rojo Jan 11 '14 at 00:59