9

Here is my code.

{2, 9, 3, 16, 81, 0.09} //. {x_Integer -> Sqrt[x]}

The pop-up message ReplaceRepeated::rrlim suggests that this rule can be applied an infinite number of times.

I am supposed to get {Sqrt[2],Sqrt[3],Sqrt[3],Sqrt[2],Sqrt[3],0.09}

What's wrong with my code here?

Michael E2
  • 235,386
  • 17
  • 334
  • 747
kile
  • 1,671
  • 5
  • 10
  • 3
    Hint: After the first pass, what is the head of the first list element's (Sqrt[2]) contents? – ciao Feb 29 '20 at 10:16
  • 1
    @ciao, Power. How to make it work ? {Sqrt[2],Sqrt[3],Sqrt[3],Sqrt[2],Sqrt[3],0.09} – kile Feb 29 '20 at 10:18
  • 2
    One way might be {y : Power[_, _] -> y, x_Integer -> Sqrt[x]} for the rules. More importantly, do you see why the example evaluates infinitely? – ciao Feb 29 '20 at 10:20
  • 2
    @ciao,x_Integer still assumes Sqrt[2] and Sqrt[3] are Integer. – kile Feb 29 '20 at 10:26
  • That pattern matches the 2 in Sqrt[2], does it not? So that 2 is transformed to Sqrt[2], which is then "inside" the original Sqrt, leading to Sqrt[Sqrt[2]]. ReplaceRepeated is effectively the fixed point of ReplaceAll, which will drill into expressions until a pattern match is (or is not) found.

    The result you are getting is exactly what you should be getting. You need to provide the pattern to match the parts to be left alone, as in my comment example.

    A review of the documentation for the various Replace species will be helpful here.

    – ciao Feb 29 '20 at 10:29
  • This works {2, 9, 3, 16, 81, 0.09} /. x_Integer -> Sqrt[x] – OkkesDulgerci Feb 29 '20 at 12:34
  • @OkkesDulgerci, I thought you misunderstood my question here. Please look at what others had done here. – kile Feb 29 '20 at 12:44
  • 4
    Perhaps a better response to @OkkesDulgerci would be to point out that his code changes 9 to 3, not to Sqrt[3] as desired. – Michael E2 Feb 29 '20 at 14:36
  • {2, 9, 3, 16, 81, 0.09} //. {x___, y_Integer, z___} :> {x, Sqrt[y], z} is another way to replace only at the first level. – LouisB Mar 01 '20 at 21:04

2 Answers2

14

I think the proper answer here is to use FixedPoint, because //. dives in to the expression. Sqrt[2] contains an integer inside so it replaces it over and over.

FixedPoint[Replace[#, x_Integer :> Sqrt[x], {1}] &, {2, 9, 3, 16, 81, 0.09}]

Notice that I specifically said that it should replace at level 1.

SHuisman
  • 3,258
  • 8
  • 12
  • 2
    Can you please point to the place in the documentation where it is written that //. should dive into subexpressions. – yarchik Feb 29 '20 at 11:28
  • It is basically doing /. repeatedly until it doesn't change any more. The documentation of /. (ReplaceAll). Which has as first line: applies a rule or list of rules in an attempt to transform each subpart of an expression expr. – SHuisman Feb 29 '20 at 11:30
  • 3
    @yarchik "expr//.rules effectively applies /. repeatedly, until the results it gets no longer change." (Docs for ReplaceRepeated) See also the documentation for the error message – Michael E2 Feb 29 '20 at 14:38
  • 1
    @MichaelE2 Why then this {2, 9, 3, 16, 81, 0.09} //. {x_ /; (Head[x] == Integer) -> Sqrt[x]} does not work? Sorry, if it is something obvious. – yarchik Feb 29 '20 at 18:12
  • 2
    it works, but again, it dives deeper in to the expression because you're using //. (or /.) – SHuisman Feb 29 '20 at 20:54
  • @yarchik For the same reason the OP's {2, 9, 3, 16, 81, 0.09} //. {x_Integer -> Sqrt[x]} fails (since they're equivalent, no?). Maybe you can follow the steps with a print statement inserted: ReplaceRepeated[{2, 9, 3, 16, 81, 0.09}, {x_ /; (Head[If[ListQ[x], Print[FullForm /@ x]]; x] == Integer) :> Sqrt[x]}, MaxIterations -> 4] – Michael E2 Mar 01 '20 at 01:45
  • @MichaelE2 Yes, now it is clear. I think Mr.Wizard solution is the closest expression of the OP idea. – yarchik Mar 01 '20 at 09:18
12

The documentation specifically states:

You should be very careful to avoid infinite loops when you use the //. operator. The command x //. x -> x + 1 will, for example, lead to an infinite loop.

Since only the first rule that matches is applied to a given expression you can, as ciao commented, use:

{2, 9, 3, 16, 81, 0.09} //. {skip_Power :> skip, x_Integer :> Sqrt[x]}
{Sqrt[2], Sqrt[3], Sqrt[3], Sqrt[2], Sqrt[3], 0.09}

Recommended reading:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    Very nice solution, exactly as was asked in OP. Maybe one can add that some confusion arises due to the fact that Replace has levelspec option, whereas ReplaceAll and ReplceRepeated---no. – yarchik Mar 01 '20 at 09:16
  • 1
    A slight generalization: list //. {x_Integer :> Sqrt[x], skip_?NumericQ :> With[{x = Simplify[skip]}, If[IntegerQ[x], Sqrt[x], skip]]}. In case other numeric expression are in the original list. – Michael E2 Mar 01 '20 at 18:04