The answers to this question provide code for a function I'll call postOrderReplaceAll
to replace from the inside-out:
postOrderReplaceAll[expr_, rules_] :=
Fold[Replace[#1, rules, {#2}] &, expr, Reverse@Range[0, Depth@expr]];
It operates on the innermost expression first, and works outward (depth-first postorder), in contrast to the behavior of ReplaceAll
, which starts with the biggest expression and then if necessary looks at its parts (depth-first preorder). (See this question and this wiki page for some background.)
This function keeps applying the pattern whenever it can match it, in contrast to ReplaceAll
which, once it has found an expression to change, doesn't probe any more:
ReplaceAll[f[f[f[1]]], f[x_] :> g[x] ]
(*Result: g[f[f[1]]]*)
postOrderReplaceAll[f[f[f[1]]], f[x_] :> g[x] ]
(*Result: g[g[g[1]]]*)
I would like to make a function postOrderReplaceInnermost
that, like ReplaceAll
, makes one replacement per subtree, if there is any to be made. How to do it? (And, any suggestions for a better name?)
Examples of desired behavior:
postOrderReplaceInnermost[f[f[f[1]]], f[x_] :> g[x] ]
(*Result: f[f[g[1]]]*)
postOrderReplaceInnermost[f[f[f[1]]] + f[f[1]], f[x_] :> g[x] ]
(*Result: f[f[g[1]]] + f[g[1]]*)
I think this might be difficult to achieve with the given code, since it iterates over level (and in example 2, the two terms have different depth).
Here is my attempt, which gets partway there. It uses Throw
and Catch
to stop the Fold
once a replacement has been made:
postOrderReplaceDeepestLevel[expr_, rules_] :=
Catch@Fold[
If[! SameQ[Replace[#1, rules, {#2}], #1],
Throw[Replace[#1, rules, {#2}]], #] &, expr,
Reverse@Range[0, Depth@expr]];
But because the command iterates by level, it stops as soon as it has made a single replacement anywhere, instead of continuing to make one replacement in the first term f[f[1]]
:
postOrderReplaceDeepestLevel[f[f[f[1]]] + f[f[1]], f[x_] :> g[x]]
(* Result: f[f[1]] + f[f[g[1]]]*)
postOrderReplaceDeepestLevel[%, f[x_] :> g[x]]
(* Result: f[g[1]] + f[g[g[1]]]*)
Plus
, you can dopostOrderReplaceDeepestLevel[expr1_ + expr2_, rules_] := postOrderReplaceDeepestLevel[expr1, rules] + postOrderReplaceDeepestLevel[expr2, rules]
$\endgroup$f[f[h[1]]] + f[f[1]
would be a good test case to add, to show that you don't just want to apply the rule to level-2
. $\endgroup$Plus[f[f[f[1]]],f[f[1]]]
, so when the deepestf[1]
has been replaced, the rule has been used "inside", so it should stop, but that's clearly not what OP wants. $\endgroup$Plus[f[f[f[1]]],f[f[2]]]
for clarity. The rule is applied tof[1]
to giveg[1]
. Then it is not applied to any part off[f[g[1]]]
because each of thosef
expressions contains a successful application of the rule. However it is applied tof[2]
because there is no successful application of the rule inside that expression. Likewise it is then not applied tof[g[2]]
again, because there's a successful application inside thef
already. $\endgroup$