6

From Mathematica's own documentation:

{g[1],Hold[g[1]]}/.g[n_]:>n+1

leads to

{2, Hold[1 + 1]}

My question: is there a way to protect subexpressions from being replaced by ReplaceAll? I am using composite constructs as variables, like

v[a, b]

and would like to be able to do stuff like this

v[a, b] + a - b /. {a -> x, b -> y}

leading to

v[a, b] + x - y

and not

v[x, y] + x - y

without complicated patterns. Unfortunately, using Replace and level specifications is not option.

This idiom

v[a, b] + a - b /. {catch_v -> catch, a -> x, b -> y}

works, as 'catch_v -> catch' prohibits the subsequent rules to be applied to v. But I would rather like to prohibit the replacement on the expression level (with some kind of Hold or HoldForm expression). Is this possible?

phantomas1234
  • 470
  • 3
  • 13
  • 1
    Please see this question, especially Alexey's answer, which is exactly what you are asking for: http://stackoverflow.com/questions/6451802/pattern-to-match-only-children-of-certain-elements In short, you can replace `Hold` by itself, which will prevent further replacements: `{g[1], Hold[g[1]]} /. {e : Hold[___] :> e, g[n_] :> n + 1}` – Szabolcs Nov 15 '11 at 08:19
  • Well, that's pretty much what I discarded as a possible solution (v[a, b] + a - b /. {catch_v -> catch, a -> x, b -> y} in my question; see above). Anyway, thank you for the link, somehow I overlooked this question. – phantomas1234 Nov 16 '11 at 18:43
  • Sorry about that, I overlooked it ... – Szabolcs Nov 16 '11 at 18:43

2 Answers2

5

I am not aware of anything like this "out of the box", but one can achieve a similar effect by hiding the expressions where one does not want replacements, with some temporary symbols, then applying the rules, and then restoring those expressions back. Here is one way:

ClearAll[ReplaceProtect];
ReplaceProtect /: f_[ReplaceProtect[expr_, ptrn_], args___] :=
   Module[{protectRules, ps, n = 0},
      protectRules = 
         Cases[expr, x : ptrn :> (HoldPattern[x] -> ps[n++]), Infinity];
      f[expr /. protectRules, args] /. 
         Replace[protectRules, 
            Verbatim[Rule][Verbatim[HoldPattern][x_], rhs_] :> (rhs :> x), {1}
         ]  
   ]

Here is how one can use this:

In[42]:= ReplaceProtect[{g[1],Hold[g[1]]},_g]/.g[n_]:>n+1
Out[42]= {g[1],Hold[g[1]]}

In[43]:= ReplaceProtect[{g[1],Hold[g[1]]},_Hold]/.g[n_]:>n+1
Out[43]= {2,Hold[g[1]]}

In[44]:= ReplaceProtect[v[a,b]+a-b,_v]/.{a->x,b->y}
Out[44]= x-y+v[a,b]

I defined ReplaceProtect with UpValues in a rather general way, so it can be used also with Replace and other functions which use rules.

Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • Leonid, I assumed that his replacements may involve patterns rather than symbols, and therefore the entire expression must be made inert. I cannot think of a better way to do this than Strings (with or without Compress). – Mr.Wizard Nov 14 '11 at 20:57
  • @Mr.Wizard My solution seems to address the general case as well. You simply indicate the pattern that the parts in need of "protection" must satisfy, and the pieces of expression matching that pattern get protected (The fact that in examples I used a pattern of the form `_Symbol` is a coincidence. Any pattern can be used). As for strings, I try to avoid conversion to strings and back, since I feel it is rather fragile, and somehow not right, given Mathematica metaprogramming facilities and its support for the code-as-data paradigm (not to detract from your answer, of course). – Leonid Shifrin Nov 14 '11 at 21:03
  • It seems I failed to understand your answer. I shall read it again. – Mr.Wizard Nov 14 '11 at 21:12
  • By the way, do you feel any better about `Compress` than `ToString`? – Mr.Wizard Nov 14 '11 at 21:16
  • @Mr.Wizard Yes I do, did not see it first - +1. But, this will induce the performance hit. – Leonid Shifrin Nov 14 '11 at 21:20
  • Leonid and Mr.Wizard, as usual, I am impressed by your meta programming skills and Mathematica wisdom. However, why should I choose your solutions over v[a, b] + a - b /. {catch_v -> catch, a -> x, b -> y}, practically speaking? In one case I have to specify a pattern for the parent expression (Leonid's solution), in the other case, I have to wrap something around the parent expression (Mr.Wizard's solution). I guess there is really nothing "out-of-the-box". – phantomas1234 Nov 16 '11 at 19:08
  • @phantomas1234 "However, why should I choose ..." You shouldn't. The solution you mention is perfectly valid and simpler than either of ours. If you would not discard this way yourself in your question, I'd be the first to support it. I took it as you'd like to not think about your (list of) rules, and just apply them. If you are eager to modify your rules, the solution of prepending an idle rule is probably the best. – Leonid Shifrin Nov 16 '11 at 19:40
5

Since ReplaceAll is designed to "transform each subpart of an expression" and is often used specifically for the ability to operate inside of Hold variations, you will need to use an inert form for your expression.

One method is to convert your expression into a string. Here is an example:

SetAttributes[holdAsString, HoldFirst]

holdAsString[expr : Except[_String]] := 
 holdAsString @@ {ToString[Unevaluated@expr, InputForm]}

holdAsString[v[a, b]] + a - b /. {a -> x, b -> y} /. holdAsString -> ToExpression
x - y + v[a, b]

Another method is to use Compress and Uncompress. This may prove more robust.

SetAttributes[holdCompressed, HoldFirst]

holdCompressed[expr : Except[_String]] := 
 holdCompressed @@ {Compress@Unevaluated@expr}

holdCompressed[v[a, b]] + a - b /. {a -> x, b -> y} /. holdCompressed -> Uncompress
x - y + v[a, b]
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125