13
$\begingroup$

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*)
$\endgroup$

1 Answer 1

11
$\begingroup$

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] *)
$\endgroup$
19
  • $\begingroup$ Now what about making some kinda wrapper compute[Expand[(a + b + c)^3]] that will produce the same result without specifying the variables manually? $\endgroup$
    – swish
    Commented Dec 25, 2012 at 14:46
  • $\begingroup$ @swish See my edit $\endgroup$ Commented Dec 25, 2012 at 14:49
  • $\begingroup$ Why not use HoldForm? Then it looks slightly nicer. $\endgroup$ Commented Dec 25, 2012 at 14:51
  • 1
    $\begingroup$ @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. $\endgroup$ Commented Dec 25, 2012 at 14:53
  • $\begingroup$ 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. $\endgroup$
    – swish
    Commented Dec 25, 2012 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.