# Using a predicate to control the application of a rule in a replacement

How can I make a replacement rule that will only be applied to those parts of an expression that will not break a condition placed on the whole expression?

For example, suppose I have $\frac{xy}{(x-y)(x+y)}$ and I want to change $y$ to $x$ getting the result $\frac{x^2}{(x-y)2x}$; i.e., without turning original expression into infinity. Further, I don't want the replacement to raise warnings or errors.

At the least, I need a way to do a replacement just once, which seems like it should be very easy, but I have no idea how to do it.

I came up with

ReplaceOnce[expr_, rule_Rule] := MapAt[# /. rule &, expr, Position[expr, rule[[1]]][[1]]]


How can I get to my goal from here?

• I don't think this is well specified. For example, what if by turning some subset of the "x" you get the warning but with some others not, how do you decide which changes you want? – Rojo Mar 2 '13 at 16:07
• Or you want "find the x in whatever order and one by one try changing them and checking if the result is valid"? – Rojo Mar 2 '13 at 16:07
• @Rojo One instance will be sufficient but if it will return every possible valid result that would be awesome. – swish Mar 2 '13 at 16:11

Not perfect, but a start (I cannot quickly add a working FixedPoint at the end) :

   repl[expr_, rule_Rule] := Module[{ex, fun},
fun[ex_] := Union[Flatten[Quiet[Select[
(ReplacePart[ex, #1 -> rule[[2]]] & ) /@
Position[ex,
rule[[1]]], FreeQ[#1, ComplexInfinity] & ]]]];
Union[Flatten[fun /@ fun[expr]]]];
repl[expr, y -> x]


(* ==> *)

{x/(2 (x - y))}


ReplaceOnce[expr_, rule_Rule] :=
MapAt[# /. rule &, expr, #] & /@ Position[expr, rule[[1]]]

ReplaceIf[expr_, rule_Rule, cond_] :=
NestWhile[
Flatten[(Select[ReplaceOnce[#, rule], cond]) & /@ #] &, {expr},
cond[#] && # != {} &, 1, \[Infinity], -1] // Quiet //
DeleteDuplicates

ReplaceIf[(x y)/((x + y) (x - y)), y -> x, # =!= ComplexInfinity &]
(*{x/(2 (x - y))}*)

• If this is based on Rolf Mertig's answer, as it appears to be, you should say so. – m_goldberg Mar 2 '13 at 18:04