Based on solutions proposed by @Leonid and @WReach I have noticed something peculiar with the use of Block and With for the Trott-Strzebonski solution.
Consider the following code:
f[x_] := x^2; g[x_] := x^3; Hold[{Hold[2.], Hold[3.]}] /. n_Real :> With[{eval = (g@*f)[n]}, Print[eval]; eval /; True ]
this will yield:
(* 64. 729. Hold[{Hold[64.], Hold[729.]}] *)
using Block will give the same result as above.
Hold[{Hold[2.], Hold[3.]}] /. n_Real :> Block[{eval = (g@*f)[n]}, Print@eval; eval /; True ] (* 64. 729. Hold[{Hold[64.], Hold[729.]}] *)
However, things become strange - as @Leonid pointed out - with the use of Composite expression in Condition:
using With prevents the evaluation of Print
Hold[{Hold[2.], Hold[3.]}] /. n_Real :> With[{eval = (g@*f)[n]}, (Print[eval]; eval) /; True ] (* Hold[{Hold[Print[64.]; 64.], Hold[Print[729.]; 729.]}] *)
However, with the use of Block the Print statements get evaluated
Hold[{Hold[2.], Hold[3.]}] /. n_Real :> Block[{eval = (g@*f)[n]}, (Print@eval; eval) /; True ] (* 64. 729. Hold[{Hold[64.], Hold[729.]}] *)
Now one begins to wonder how RuleCondition - as mentioned by @WReach - will behave with Composite expression
Hold[{Hold[2.], Hold[3.]}] /. n_Real :> RuleCondition[Print[(g@*f)[n]]; (g@*f)[n]] (* 64. 729. Hold[{Hold[64.], Hold[729.]}] *)
It turns out that RuleCondition behaves in a similar way to Block
So perhaps not a million dollar but a $5 question: Which one to use (Block, RuleCondition or With)?
In my opinion both have their own advantages. For instance, With will allow you to evaluate a part of the expression and at the same time inject unevaluated code in the substitution process.
With will enable code injection
Hold[{Hold[2.], Hold[3.]}] /. n_Real :> With[{eval = (g@*f)[n]}, (g[eval]; eval) /; True ] (* Hold[{Hold[g[64.]; 64.], Hold[g[729.]; 729.]}] *)
In the example above (in spirit with what @Leonid mentioned) we substituted the Reals and also injected code that remained unevaluated i.e. g[64] and g[729]
Block and RuleCondition together with Sow and Reap
However, if the goal is to evaluate some code while making substitution 'Block` can be quite effective:
Reap[Hold[{Hold[2.], Hold[3.]}] /. n_Real :> Block[{eval = (g@*f)[n]}, (Sow[PrimeQ@eval]; eval) /; True ]][[2, 1]] ]; (* {False,False} *) Reap[ Hold[{Hold[2.], Hold[3.]}] /. n_Real :> Block[{eval = (g@*f)[n]}, (Sow[EvenQ[Round@eval]]; eval) /; True] ] (* {Hold[{Hold[64.], Hold[729.]}], {{True, False}}} *)
In the example above we could the result whether eval generates a Prime whenever the substitution is made. Likewise, this can be achieved using RuleCondition.