8
$\begingroup$

Question

How to make the replacement $f[x] \to f[y]$ for an abitrary function $f$ if $x$ was assigned a value before?

Example 1

How can I make the replacement $x +1 \to y + 1$ if I have assigned the variable $x$ a value before?

Problematic attempts

A first guess would be

x = 1;
x + 1 /. x -> y

4

This does not give $y + 1$ because $x + 1$ is evaluated to 4 before the replacement is attempted. I expected this would give $y+1$:

Unevaluated[1 + x] /. x -> y

4

My reasoning was that the replacement of $x$ with $y$ in

x + 1 // FullForm 
Unevaluated[1 + x] // FullForm

4

Unevaluated[Plus[1,x]]

gives '4' and Unevaluated[Plus[1,y]], but it does not. Why? Is there a solution that works in this spirit?

Working Solution

In the post Symbolic computations with already assigned variables, I found the suggestion to use block. Indeed it works

Block[{x}, x + 1 /. x -> y]

y + 1

Example 2

Let

x = {1, 3, 4};
f[x_] := Length[x];

How to replace x in f[x] with {1, 2}? We allready saw that f[x] /. x -> {1, 2} will not work. Here the construction with Block does not work either

Block[{x}, f[x] /. x -> {3, 2}

0

It gives 0 since the lefthand side of the rule is evaluated first, and gives 'Length[x] = 0'. Only then the rule is applied. I guess I need a rule in the spirit of the 'problematic attempts' in order to make the desired replacement.

$\endgroup$

2 Answers 2

9
$\begingroup$

All you need is to ensure that the replacing is done before evaluation, including the LHS of the replace rule. Here is one way to achieve that, which I think is probably the most straightforward:

x = 1;
ReleaseHold[Hold[x + 1] /. HoldPattern[x] -> y]

This will also work for your second example:

ReleaseHold[Hold[f[x]] /. HoldPattern[x] -> {3, 2}]

Using Block for similar tasks is often more elegant, but as you have learned needs to be used with some care. I usually prefer solutions which more explicitly state what they try to achieve and here what you want to do is to change the usual evaluation order which I think can be read clearly from the above lines. Here is another way suggested by Mr.Wizard which is also more elegant and achieves the same thing, again with making use of some evaluation automatisms which might not be obvious to every reader:

Unevaluated[f[x]] /. HoldPattern[x] -> {3, 2}

For more details I'd recommend to have look at "tutorial/Evaluation" in the documentation which I consider a "must know" for everyone trying to get serious work done with Mathematica...

$\endgroup$
4
  • 2
    $\begingroup$ I usually use: Unevaluated[x + 1] /. HoldPattern[x] -> y $\endgroup$
    – Mr.Wizard
    Commented May 22, 2013 at 10:13
  • $\begingroup$ Can someone explain to me why this is useful? What I always have a tendency to do is to Clear[f] or Clear[f,x] and then there's nothing to worry about. What is the advantage of the more sophisticated approaches? $\endgroup$
    – bill s
    Commented May 22, 2013 at 10:20
  • $\begingroup$ @Mr. Wizard: good point, it is somewhat more elegant, but as I have now added, it also is somewhat less explicit about how exactly it changes the evaluation order. Not a problem if you have a firm grasp of it, as I guess is the case for you :-). I added it to my answer... $\endgroup$ Commented May 22, 2013 at 10:21
  • $\begingroup$ @bills: when you start to write code which you or even others try to reuse, then it is in general best to try to make as few changes to the global state of your session as possible. If you e.g. call a function which clears certain global variables with Clear that might not be what the caller expects and break the callers session/code. When writing packages and making use of name spaces, you also might need to take extra care to Clear the correct symbols. $\endgroup$ Commented May 22, 2013 at 10:28
9
$\begingroup$

As described by Albert Retey you need to hold both the expression to be operated on and the left-hand side of the replacement rule. I usually use Unevaluated:

Unevaluated[x + 1] /. HoldPattern[x] -> y

I suppose I have to at least reference this popular one:

y /. x_ :> x + 1

The x in x_ is held because it is a Pattern name and on the right because of RuleDelayed having HoldRest.

Another method that can be useful is to dynamically redefine the attributes of these functions, rather than Blocking a certain group of symbols; this therefore works for any expression without having to specify which symbols to protect.

SetAttributes[withHeldReplace, HoldAll]

withHeldReplace[expr_] :=
  With[{fns = {Replace, ReplaceAll, Rule, RuleDelayed}},
    Internal`InheritedBlock[fns,
      SetAttributes[fns, HoldFirst];
      expr
    ]
  ]

Now:

withHeldReplace[
  x + 1 /. x -> y
]
1 + y

Note: I just found an earlier use by Szabolcs of a similar method: ReplaceAll inside an Iterator


Here is an alternative for anyone uncomfortable with modifying the behavior of a System` function, even in a Block:

SetAttributes[withHeldReplace2, HoldAll]

withHeldReplace2[expr_] := Unevaluated[expr] /.
  (r: Rule | RuleDelayed | Replace | ReplaceAll)[lhs_, rhs__] :> r[Unevaluated[lhs], rhs]

x = 1;

withHeldReplace2[
  x + 1 /. x -> y
]
1 + y
$\endgroup$
4
  • $\begingroup$ wow... is that safe though? I did some experimenting with setting attributes to System` functions, but giving Rule the attribute HoldAllComplete causes problems in the front end right away. If it is safe, I suggest using InheritedBlock in your positionFunction answer to give HoldPattern HoldAllComplete. Lets see if I can find out more about InheritedBlock... $\endgroup$ Commented May 22, 2013 at 10:26
  • 2
    $\begingroup$ @Jacob I think it's safe but I have not tested it extensively. I have set HoldFirst on Rule before (within InheritedBlock) and it seemed to work without issue, but this isn't documented after all. By the way I prefer to focus on the higher-level issues of "PositionFunction" before getting down to these details, but I was thinking about that too which is why this came to mind. I'll surely be interested in what you learn about this as you're causing me to think through some interesting aspects of the language. $\endgroup$
    – Mr.Wizard
    Commented May 22, 2013 at 10:34
  • $\begingroup$ I think it is only safe as long as you don't use any Dynamic. As the FrontEnd itself might use these functionalities, it probably is only safe when used without a frontend altogether. Here is an example where you can see the problem: x=5;Dynamic[x -> DateString[], UpdateInterval -> 1] and then, in an extra cell: Internal`InheritedBlock[{Rule}, SetAttributes[Rule, HoldFirst]; Pause[5]]. I would in general be very cautious to change system symbols, even in Block and IneritedBlock, especially when also working with Dynamic... $\endgroup$ Commented May 22, 2013 at 13:06
  • $\begingroup$ ... the reason for all that is that when the preemptive links interupt the queued evaluation they obviously see the current state of those Blocked symbols. This can cause problems which are very hard to detect and debug. I would wish that this simply wouldn't be the case, but probably that would make preemptive evaluations too expensive/complicated or whatever. This should, IMHO, certainly be better documented... $\endgroup$ Commented May 22, 2013 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.