5

A light-weight question for the experts. I can't seem to figure the correct syntax to this replacement. I have this list

Clear[a, b, c, d]
polesList = {{3, {a, b}}, {5, {c, d}}};

It is of the form of a list with sublists each have the form {order,{x,y}} and I want to generate a new list of this form (x+y)^order

Currently this is what I do, which works:

((#[[2, 1]] + #[[2, 2]])^#[[1]]) & /@ polesList

(* ----->   {(a + b)^3, (c + d)^5}  *)  

But I have been trying to learn to use ReplaceAll as it is more clear to me than pure functions, since I can see the pattern better, like this:

Clear[a, b, c, d, n]
polesList = {{3, {a, b}}, {5, {c, d}}};
ReplaceAll[polesList, {n_, {x_, y_}} :> (x + y)^n]   (*I thought this will work*)

I get strange result, which is

{(5 + c)^3, {(5 + d)^a, (5 + d)^b}}

What is the correct syntax to do this replacement using ReplaceAll instead of the pure function method?

Thanks

Update:

I find that using Replace, instead of ReplaceAll works, but need to say {1} at the end:

Clear[a, b, c, d, n]
polesList = {{3, {a, b}}, {5, {c, d}}};
Replace[polesList, {n_, {x_, y_}} :> (x + y)^n, {1}]

which gives

{(a + b)^3, (c + d)^5}

But ReplaceAll does not take {1} at the end. I am more confused now which to use :)

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
Nasser
  • 12,849
  • 6
  • 52
  • 104
  • I think that `ReplaceAll[expr,rules]` is essentially equivalent to `Replace[expr,rules,Infinity]`. Replacing at all levels is a more common thing than just at a single level. That's why `ReplaceAll` has a infix form `/.` and `Replace` doesn't. – Simon Aug 01 '11 at 02:44
  • 2
    Apparently the order that `Replace` and `ReplaceAll` [traverse the expression tree is different](http://www.verbeia.com/mathematica/tips/HTMLLinks/Tricks_P-Z_10.html). `Replace` starts at the lowest level while `ReplaceAll` starts at the highest. Compare `Replace[h[f1[a1], f2[e][a2]], (a_ /; Print[a] :> 0), Infinity]` with `ReplaceAll[h[f1[a1], f2[e][a2]], (a_ /; Print[a] :> 0)]`. – Simon Aug 01 '11 at 02:52
  • This is why in your case (`polesList = {{3, {a, b}}, {5, {c, d}}}`): `Replace[polesList, {n_, {x_, y_}} :> (x + y)^n, Infinity]` works, but `ReplaceAll[polesList, {n_, {x_, y_}} :> (x + y)^n]` does not. – Simon Aug 01 '11 at 02:54
  • 4
    This Leonid's answer is strongly relevant: ["Why do Replace and ReplaceAll give different results even when only one rule and one expression is used?"](http://stackoverflow.com/questions/6836790/why-do-replace-and-replaceall-give-different-results-even-when-only-one-rule-and/6836972#6836972) – Alexey Popkov Aug 01 '11 at 02:58
  • @Alexey: I'd even voted up that answer of Leonid... How could I forget his post so quickly?! – Simon Aug 01 '11 at 03:40
  • 1
    @Simon Perhaps it was not long enough :) – Leonid Shifrin Aug 01 '11 at 13:43
  • @Simon Also, thanks for the link. I was unaware of the discussion of `Replace` at Ted Ersek's site (although of course aware of the site itself), otherwise I would have given a link to it in my answer. – Leonid Shifrin Aug 01 '11 at 17:01

3 Answers3

8

The problem is that ReplaceAll looks at all levels in the expression and the first match to the pattern

{n_, {x_, y_}}

in the expression {{3, {a, b}}, {5, {c, d}}} is

{ n=={3, {a, b}}, {x==5, y=={c, d}}}

(if that notation is clear)

So you got the "strange" result

(5 + {c,d})^{3, {a, b}} == {5+c, 5+d}^{3, {a, b}} 
== {(5+c)^3, (5+d)^{a, b}} == {(5+c)^3, {(5+d)^a,(5+d)^b}}

The easiest fix, if n is always numeric, is

In[2]:= {{3, {a, b}}, {5, {c, d}}} /. {n_?NumericQ, {x_, y_}} :> (x + y)^n
Out[2]= {(a + b)^3, (c + d)^5}

Where I used the shorthand /. for ReplaceAll.


It might be that using Replace at level 1 is the best option

In[3]:= Replace[{{3, {a, b}}, {5, {c, d}}}, {n_,{x_,y_}}:>(x+y)^n, {1}]
Out[3]= {(a+b)^3,(c+d)^5}

which should be compared with the default replace that works at the top level {0}

In[4]:= Replace[{{3, {a, b}}, {5, {c, d}}}, {n_,{x_,y_}}:>(x+y)^n]
Out[4]= {(5+c)^3,{(5+d)^a,(5+d)^b}}
Simon
  • 14,631
  • 4
  • 41
  • 101
  • `Plus@@@polesList[[All, 2]]^polesList[[All, 1]] == {(a + b)^3, (c + d)^5}` – Simon Aug 01 '11 at 02:26
  • +1, Just saw your answer, thanks. I see I can use Replace with {1} works, but wanted to see if I can use ReplaceAll. I guess I have to use your solution with NumericQ added to make ReplaceAll work. – Nasser Aug 01 '11 at 02:29
  • +1, with some minor quibbles... the default level spec for `Replace` is `{0}`, the entire expression. A level spec of `2` tries replacements on both level 1 and level 2, whereas `{1}` operates only upon level 1 which is what is wanted here – WReach Aug 01 '11 at 02:34
  • @WReach: Yeah - I fixed that just before you posted your comment! – Simon Aug 01 '11 at 02:37
8

The problem is that ReplaceAll inspects all levels of the expression when looking for replacements. The entire expression matches the pattern {n_, {x_, y_}} where:

n matches {3, {a, b}}

x matches 5

y matches {c, d}

So you end up with (5 + {c , d}) ^ {3, {a, b}} which evaluates to the result you see.

There are a few ways to fix this. First, you can change the pattern so that it does not match the outermost list. For example, if the n values are always integers you could use:

ReplaceAll[polesList, {n_Integer, {x_, y_}} :> (x + y)^n]

Or, you could use Replace instead of ReplaceAll, and restrict the pattern matching the first level only:

Replace[polesList, {n_, {x_, y_}} :> (x + y)^n, {1}]

I find that applying replacement rules to the first level of a list is very common. It so happens that Cases, by default, only operates on that level. So I find myself frequently using Cases for level one replacements when I know that all elements will match the pattern:

Cases[polesList, {n_, {x_, y_}} :> (x + y)^n]

This last expression is how I would probably write the desired replacement. Keep in mind, though, that if all elements do not match the pattern, then the Cases approach will drop the mismatches from the result.

WReach
  • 18,098
  • 3
  • 49
  • 93
  • 2
    +1 for `Cases` with a `:>`. It's a good construct that I only started using in the last year. – Simon Aug 01 '11 at 02:36
  • Thanks. may be I should think of using Replace instead of ReplaceAll and specify the level I want. It is more explicit this way. I forgot about Replace with levels, was just using ReplaceAll. – Nasser Aug 01 '11 at 02:39
  • I find the `Cases` construct dangerous. I enjoy terse code, and I am aware of this method, however, I believe that code should be logical, and `Cases` is a filtering function; further, it silently fails (if you are expecting it to match all elements in a list) and that is reason enough for me to avoid using it in regular code. If `Replace` and `ReplaceAll` are unpalatable for whatever reason, I would use: `f[{n_, {x_, y_}}] := (x + y)^n; f /@ polesList` or similar. – Mr.Wizard Aug 16 '11 at 01:32
8

You could also use ReplaceAll[ ] with Map:

Map[ReplaceAll[#, {n_, {x_, y_}} :> (x + y)^n] &, polesList]

or (using shorthands increasingly)

ReplaceAll[#, {n_, {x_, y_}} :> (x + y)^n] & /@ polesList

or

# /. {n_, {x_, y_}} :> (x + y)^n & /@ polesList
Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190