Here is another solution, that works with a list of rules and with held expressions
cRepRep2[expr_, snd_] :=
{Last@#, Length@# - 2} &@
FixedPointList[# /. snd &, expr]
Here is a similar solution, that saves on memory by using FixedPoint
rather than FixedPointList
. It also works with held expressions
cRepRep3[expr_, snd_] :=
Module[
{c = -1, resExpr},
resExpr =
FixedPoint[# /. snd &, expr, SameTest -> ((c++; SameQ[#, #2]) &)];
{resExpr, c}
]
Below is the dirty way made into a function, which is less nice than the other solutions.
cRepRep[expr_, (Rule | RuleDelayed)[pat_, rep_]] :=
Module[{c = 0},
{ReplaceRepeated[expr, pat :> (c++; rep)], c}
]
Here is function that tries to let all the replacements be done by ReplaceRepeated
, while also working for held expressions. Unfortunately there is a problem with it, which it shares with cRepRep
, as we will see further below.
cRepRep4[expr_, (Rule | RuleDelayed)[pat_, rep_]] :=
cRepRep4[expr, {pat :> rep}]
cRepRep4[expr_, rules_List] :=
Module[{c = 0, rLen, cIncrCondArrHeld, repsHeld, newRepsHeld,
newRulesHeld, resExpr},
rLen = Length[rules];
cIncrCondArrHeld =
DeleteCases[Hold@Evaluate@ConstantArray[Hold[c++; True], rLen],
Hold, {2, Infinity}, Heads -> True];
repsHeld = Hold[rules][[All, All, 2]];
newRepsHeld =
Apply[
Condition,
Hold@Evaluate@Thread[Join[repsHeld, cIncrCondArrHeld]], {2}];
newRulesHeld =
Apply[RuleDelayed,
Hold@Evaluate@Thread@Prepend[newRepsHeld, rules[[All, 1]]], {2}];
resExpr =
ReplacePart[
{
expr,
newRulesHeld
}
,
{0 -> ReplaceRepeated, {2, 0} -> Unevaluated}
];
{resExpr, c}
]
Unfortunately ReplaceRepeated
can terminate in two ways, which causes trouble. Either it makes a last replacement which results in the same expression, or all the rules fail to match. I think it is not straightforward, if at all possible, to get this information from ReplaceRepeated
. Therefore cRepRep
, cRepRep4
will fail, like in the following example.
c = 0; (*just to make things harder*)
cRepRep4[HoldComplete@Hold@Hold@c, {Hold[c] :> c}]
cRepRep4[number, {9-> 8, sumrule}]
{HoldComplete[c], 2} (*correct*)
{8, 5} (*count is one too high*)
Mr.Wizard's solution does not suffer from this because the replacement by the rules is really done by ReplaceAll
and the rule in ReplaceRepeated
always matches, so that ReplaceRepeated
always terminates because of "identical expressions".
Good examples
c = 3; (*just to make things harder*)
cRepRep3[HoldComplete@Hold@Hold@c, Hold[c] :> c]
cRepRep3[number, {9 -> 8, sumrule}]
{HoldComplete[c], 2}
{8, 4}
ReplaceRepeated
. $\endgroup$ – PlaysDice Feb 12 '15 at 13:29ReplaceRepeated
that demonstrate the actual number of replacement operation :) $\endgroup$ – xzczd Feb 12 '15 at 13:39