10

ReplaceAll[] might lead to unexpected behavior (at least it surprised me). I use ReplaceAll[] to convert a list (basically data cleanup).

For the command:

{{1, "2"}, {2, "3"}} /. {x_, _ } :> {x, "SOMETHING"}

I expected

{{1, "SOMETHING"}, {2, "SOMETHING"}}

but got

{{1, "2"}, "SOMETHING"}.
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Dennis Guse
  • 287
  • 1
  • 7
  • It is extremely important to remember that the pattern _ in Mathematica matches every expression. In particular {x_, _ } matches the expression {list1,list2} and gives {list1, "Something"} – Lotus Sep 26 '17 at 10:25
  • 4
    Stricter pattern matching would have saved you: {{1, "2"}, {2, "3"}} /. {x_Integer, _String} :> {x, "SOMETHING"} – J. M.'s missing motivation Sep 26 '17 at 10:26
  • 3
    @J.M. It could have saved him, but it is not good practice. /. is much too tempting because it is short. People should always default to Replace (or in some cases Lookup) and use ReplaceAll only for algebraic replacements (or when replacing at all levels is required). – Szabolcs Sep 26 '17 at 10:42
  • 1
    @Szabolcs I don't (entirely) agree with that. I think /. can often be used well given a little thought, and I find defaulting to Replace a bit heavy handed. Of course there are cases where Replace is far superior, but in many other cases I would argue that a pattern restriction as J. M. offered is entirely adequate and not bad practice. Careless use of /. is just that: carelessness, and IMHO baroque code is not a remedy for that. – Mr.Wizard Sep 26 '17 at 21:32
  • 1
    @Mr.Wizard I would not call using Replace instead of ReplaceAll baroque. The shorthand /. is a minor convenience only. I do sloppily use /. when working interactively, but when writing code which is expected to be robust, it is a good idea to use defensive programming. Even the smartest programmer makes mistakes, or fails to see consequences sometimes. It is good practice to try to make our programs predictable and to reduce the chance of mistakes. – Szabolcs Sep 27 '17 at 09:36
  • @Mr.Wizard I think that if the goal is to replace at level 1, then that's exactly what one should do, and not try to come up with a pattern that won't match at deeper level under any circumstances. What if this function is in a continually maintained package, and one year later you relax the requirements on the function's input but forget to update that special pattern that only prevents deeper-level matching if the input is of a certain kind? – Szabolcs Sep 27 '17 at 09:38
  • 1
    @Szabolcs We should discuss this another time in more detail. I probably don't disagree with you in practice. Too sleepy now, and comments aren't the place. – Mr.Wizard Sep 27 '17 at 12:10

2 Answers2

13

ReplaceAll is way overused, and it can very easily lead to unexpected results (or bugs in your code) because it replaces at all levels. In fact, the example you show is one of the most common errors stemming from the misuse of ReplaceAll.

I recommend using ReplaceAll only when you know you need it, except for algebraic replacements.

Otherwise, use Replace:

Replace[{{1, "2"}, {2, "3"}}, {x_, _ } :> {x, "SOMETHING"}, {1}]

Make Replace your default choice unless you know why you need ReplaceAll, especially when developing packages (where robustness is a requirement).

Of course, when doing interactive work, a bit of sloppiness if forgivable :-) /. is just shorter to type.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • 3
    +1. You make a very good point. It is rather surprising that this has not been emphasized enough on this site for such a long time, or may be I just don't remember. – Leonid Shifrin Sep 26 '17 at 12:31
  • "I recommend using ReplaceAll only when you know you need it, except for algebraic replacements" seems a bit extreme. I think better advice is "use as specific pattern as possible when using ReplaceAll", for example {_Integer,_} in this case. I guess my POV is the subtly different "Replace is way underused". It's a great function and often exactly what you need, but it isn't a substitute for ReplaceAll. – Itai Seggev Oct 02 '17 at 17:27
  • 1
    FWIW, I did some work in the last year (I think for 11.1, but I'd have to double check) expanding the ref pages for Replace, ReplaceAll, and ReplaceRepeated to both clarify what each did and the relationships between them, and I do address this point. But I think I'll add a simpler example focusing exactly on ReplaceAll based on this question. – Itai Seggev Oct 02 '17 at 17:32
  • 1
    @Itai If you are working on the documentation pages, one difference you may want to point out is that ReplaceAll works "from the outside towards the inside" and Replace works "from the inside towards the outside". Thus it is not possible to do use Replace to do precisely what ReplaceAll does. I'm not sure how to phrase this concisely and clearly :-) – Szabolcs Oct 02 '17 at 18:11
  • 1
    I suggest you look at the 4th, 5th, and 7th Properties & Relations examples for Replace. I think they are documenting exactly what your trying to say. – Itai Seggev Oct 03 '17 at 05:46
  • @ItaiSeggev I looked only under Details originally, but I can see how it is difficult to squeeze this in there. – Szabolcs Oct 03 '17 at 13:12
2

The issue is that the provided pattern also matches the outer list. Therefore ReplaceAll[] replaces the outer list and stops (as there are no elements left).

To overcome this issue, Map[] must be used:

ReplaceAll[{x_, _} :> {x, "SOMETHING"}] /@ {{1, "2"}, {2, "3"}}
MarcoB
  • 67,153
  • 18
  • 91
  • 189
Dennis Guse
  • 287
  • 1
  • 7
  • 1
    +1. Actually, David Wagner discusses exactly this case in his book, I think in the chapter on patterns and pattern-matching. It is a very common source of errors in Mathematica. – Leonid Shifrin Sep 26 '17 at 12:30