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},
Heads -> True
]
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 =
Thread[
Complement[
getsymbolsF[expr],
Thread[HoldComplete[unfrozen]]
],
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] :=
Thread[Select[depends[expr], symbolHasValue], HoldComplete] /.
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] *)
compute[Expand[(a + b + c)^3]]that will produce the same result without specifying the variables manually? – swish Dec 25 '12 at 14:46HoldFormis often invisible, when you forget about it, and can confuse. Besides, I have this as an option - theevalWithFrozenSymfunction accepts the wrapper head as a second optional argument. – Leonid Shifrin Dec 25 '12 at 14:53c=d+eand I want to substitute this definition in my computation but ifdorealso 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:02cis "defined somehow" having a value "d+e", but you want to expand it, while ifdoreare "defined somehow", you want not to. You have to define what is meant by "defined somehow". The only clear way I see here is to say that they are defined if they have been assigned purely numerical values. – Leonid Shifrin Dec 25 '12 at 15:21SetDelayedfor every definition. – swish Dec 25 '12 at 15:51SetDelayed- not necessarily. All that matters is that you define dependent variables before independent ones, see e.g. here. – Leonid Shifrin Dec 25 '12 at 16:05unfrozento yourevalWithSemiFrozenSymsinstead of{syms}and it finally works as expected, a bit slow though but fine. Thanks! – swish Dec 25 '12 at 16:27evalWithSemiFrozenSymsjust attempts to determine the symbols to be kept unfrozen automatically - if you have an explicit list, you can useevalWithFrozenSyms. But, if you usedevalWithSemiFrozenSyms, did you mean that is does not work for you as written and you had to modify it? – Leonid Shifrin Dec 25 '12 at 16:32dependsinstead ofgetHeldSymbolsin default value forgetsymbolsFright? Because it throws an error otherwise. – swish Dec 25 '12 at 16:41getHeldSymbols, so you need to take and use the new version. – Leonid Shifrin Dec 25 '12 at 16:42dependsit gets all variables and I can provide them one by one to be unfrozen. – swish Dec 25 '12 at 16:50getHeldSymbols(which is a very simplified version ofdepends, tracking only lexical dependencies),so it may matter for larger expressions / number of symbols. – Leonid Shifrin Dec 25 '12 at 16:57