2

As a simplified example:

In[780]:= f[x_] := a /. {a -> x};
f[1]

Out[781]= 1

This yields the correct result. But

In[775]:= rule = {a -> x};
f[x_] := a /. rule;
f[1]

Out[777]= x

This does not work at all. It seems to me that the two ways should be equivalent.

I want to use the second form to make the code clear. What can I do?

Idear
  • 21
  • 1

2 Answers2

4

To fully understand this answer one must know about DownValues, the documentation is a good place to start reading about this, as well as (96).

In your situation, it is better to use = instead of delayed assignment :=. Let's take a look what this means for the function definition:

f[x_] := a /. {a -> x};
DownValues[f]

{HoldPattern[f[x_]] :> (a /. {a -> x})}

f[x_] = a /. {a -> x};
DownValues[f]

{HoldPattern[f[x_]] :> x}

The function definitions should always give the same result, even though in the first case the replacement happens at runtime and in the second case the replacement happens when the function is defined.

If you use the second way you will have no problem writing the code the way you want.

rule = {a -> x};
f[x_] = a /. rule;
DownValues[f]

{HoldPattern[f[x_]] :> x}

The function gives the same result as both of the previous cases described, and the definition of the function is equivalent to the second case.

Hopefully, you now understand how to figure out why it does not work with :=. Again, we use DownValues which shows us the definition of a given symbol:

rule = {a -> x};
f[x_] := a /. rule;
DownValues[f]

{HoldPattern[f[x_]] :> (a /. rule)}

This tells us that the way the function f is being implemented in Mathematica is as a replacement rule, like this:

f[1] /. HoldPattern[f[x_]] :> (a /. rule)

x in the pattern does not appear on the right-hand side of the rule, that's why it isn't working. Symbols implicitly defined on the left-hand side of the rule must appear explicitly on the right-hand side of the rule.

C. E.
  • 70,533
  • 6
  • 140
  • 264
0

You can make use of dynamic scoping, i.e. Block.

rule = {a -> x};
f[z_] := Block[{x}, x = z; a /. rule];

f[1] now gives 1.

Practically speaking, in the way you did it at first, x on the right-hand side of f[x_] := ... is only local to the function definition. It is different from any x outside.

By wrapping x in Block[{x}, ...], we're telling Mathematica to treat x inside as the same as whatever x that appears in the execution history of the whole code.

This is only temporary though. In our example, x = z does not have a side-effect. Once the execution of Block is finished, x outside is still what it was before the execution.

Taiki
  • 5,259
  • 26
  • 34