# Symbolic computations with already assigned variables

For example I have assigned some variables that could defined in terms of each other. Then I for some reason need to derive some formula using this variables. How to show the result of this computation without substituting the values and how then substitute them?

It should look something like this:

computeWithOnly[expr_,vars_]:=...
substitute[expr_,vars_]:=...

a:=1;b:=2;c:=d+e;d:=3;e:=4;
computeWithOnly[Expand[(a+b+c)^3]]
(*a^3 + 3 a^2 b + 3 a b^2 + b^3 + 3 a^2 c + 6 a b c + 3 b^2 c +
3 a c^2 + 3 b c^2 + c^3*)
computeWithOnly[Expand[(a+b+c)^3],{c}]
(*a^3 + 3 a^2 b + 3 a b^2 + b^3 + 3 a^2 d + 6 a b d + 3 b^2 d +
3 a d^2 + 3 b d^2 + d^3 + 3 a^2 e + 6 a b e + 3 b^2 e + 6 a d e +
6 b d e + 3 d^2 e + 3 a e^2 + 3 b e^2 + 3 d e^2 + e^3*)
computeWithOnly[Expand[(a+b+c)^3],All]
(*1000*)
substitute[computeWithOnly[Expand[(a+b+c)^3],{c}],{a,b}]
(*27 + 27 d + 9 d^2 + d^3 + 27 e + 18 d e + 3 d^2 e + 9 e^2 +
3 d e^2 + e^3*)

-

### Solution using Block

The easiest option is probably to use Block, and wrap your result in Hold or HoldForm. So,

a = 1; b = 2; c = 3;

result =
Block[{a, b, c},
Hold[Evaluate[Expand[(a + b + c)^3]]]
]

(*
Hold[a^3+3 a^2 b+3 a b^2+b^3+3 a^2 c+6 a b c+3 b^2 c+3 a c^2+3 b c^2+c^3]
*)


The values will be automatically substituted once you call ReleaseHold:

ReleaseHold[result]

(* 216 *)


### Use metaprogramming to automate things

Here I will show how you can automate this step with some meta-programming. The following function returns all symbols used in building a given expression, and belonging to some pre-defined set of contexts (which I default to {"Global"}:

ClearAll[getHeldSymbols];
SetAttributes[getHeldSymbols, HoldFirst];
getHeldSymbols[expr_, contexts_: {"Global"}] :=
Cases[
Unevaluated[expr],
s_Symbol /; MemberQ[contexts, Context[s]] :> HoldComplete[s],
{0, Infinity},
]


For example:

getHeldSymbols[Expand[(a + b + c)^3]]

(* {HoldComplete[a], HoldComplete[b], HoldComplete[c]}  *)


Now, the following code defines a dynamic environment where the values of defined symbols participating in building your expression have been "frozen":

ClearAll[evalWithFrozenSyms]
SetAttributes[evalWithFrozenSyms, HoldAll];
evalWithFrozenSyms[expr_, unfrozen : {___Symbol} : {},
wrapper_: Hold, getsymbolsF_: getHeldSymbols
] :=
With[{frozen =
Complement[
getsymbolsF[expr],
],
HoldComplete
]
},
If[frozen === {}, expr,
frozen /. HoldComplete[{syms___}] :>
Block[{syms}, wrapper[##] &@expr]
]
]


Now we can use it:

evalWithFrozenSyms[Expand[(a+b+c)^3]]

(* Hold[a^3+3 a^2 b+3 a b^2+b^3+3 a^2 c+6 a b c+3 b^2 c+3 a c^2+3 b c^2+c^3]  *)


if you want to release come symbols, list them in the second argument of evalWithFrozenSyms:

evalWithFrozenSyms[Expand[(a+b+c)^3],{a,c}]

(* Hold[64+48 b+12 b^2+b^3] *)


Finally, per additional request of the OP - here is what you can do if you want to evaluate those symbols which may expand into other symbols, but not the "final" ones. We will have to use some heavy artillery now. Load the code from this answer on dependency graphs, and then here is another function:

ClearAll[symbolHasValue];
symbolHasValue[held : HoldComplete[s_]] := depends[s] =!= {held}


and finally:

ClearAll[evalWithSemiFrozenSyms]
SetAttributes[evalWithSemiFrozenSyms, HoldAll];
evalWithSemiFrozenSyms[expr_, wrapper_: Hold] :=
HoldComplete[{syms___Symbol}] :>
evalWithFrozenSyms[expr, {syms}, wrapper, depends];


Here is an example of use:

ClearAll[a, b, c, d, e, f];
a = b + c; d = e + f;
b = 1; c = 2; e = 3; f = 4;


Now, you can see that symbols a and d can be expanded, while symbols b,c,e,f should be kept frozen. Here is an example then:

evalWithSemiFrozenSyms[Expand[(a+d)^2]]

(* Hold[b^2 + 2 b c + c^2+ 2 b e +2 c e + e^2 + 2 b f + 2 c f + 2 e f + f^2] *)

-
Now what about making some kinda wrapper compute[Expand[(a + b + c)^3]] that will produce the same result without specifying the variables manually? –  swish Dec 25 '12 at 14:46
@swish See my edit –  Leonid Shifrin Dec 25 '12 at 14:49
Why not use HoldForm? Then it looks slightly nicer. –  Rolf Mertig Dec 25 '12 at 14:51
@RolfMertig HoldForm is often invisible, when you forget about it, and can confuse. Besides, I have this as an option - the evalWithFrozenSym function accepts the wrapper head as a second optional argument. –  Leonid Shifrin Dec 25 '12 at 14:53
One more good thing will be the ability to provide what symbols to actually evaluate. E.g. c=d+e and I want to substitute this definition in my computation but if d or e also defined somehow don't evaluate them if I didn't say so. And in the result also the ability to release only symbols I want. –  swish Dec 25 '12 at 15:02