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$ReplaceRepeatedthat demonstrate the actual number of replacement operation :) $\endgroup$