Analysis
In Mathematica when a definition is applied the expressions (arguments) that match pattern objects on the left-hand-side (LHS) are substituted into the matching right-hand-side (RHS) names before it is evaluated. This is separate from the evaluation that does or does not take place at the time the rule or definition is created. This substitution-before-evaluation is invariant between Set
and SetDelayed
.
Please consider this example:
ClearAll[f, a, b, x]
f[a_, b_] = (Print["one: ", a, b]; x);
x := Print["two: ", a, b];
f[1, 2]
{a, b} = {3, 4};
f[1, 2]
one: ab
two: ab
two: 34
The use of Set
in the definition creation causes the RHS to evaluate, printing "one:", but this Print
is not part of the definition created as it does not remain in the evaluated form of (Print["one: ", a, b]; x)
.
When the definition is used no a
or b
appear in the explicit unevaluated RHS (x
) therefore no substutions are made. The RHS is then evaluated and the Print
statement in the global definition of x
fires.
Between the first and second time the function is used a
and b
are given global values. When the function is next called the same evaluation sequence takes place but when x
evaluates to Print["two: ", a, b]
this time a
and b
evaluate to 3
and 4
.
Alternatives
You asked for "something more clever than f[p_,q_]:=x/.{a->p,b->q};
" but that may in essence be what you need. This code effectively performs the substitution after the RHS evaluation each time the definition is applied. If you want to use the argument values during evaluation you will need to use Block
or another function that works like Block
. Here is an example:
ClearAll[f, a, b, x]
f[aa_, bb_] := Block[{a = aa, b = bb}, x]
x = a + b;
f[1, 2]
x = 3 a + b^2;
f[1, 2]
3
7
All that remains is to make construction of these definitions easier which we can do with a little meta-programming. Here is one method:
SetAttributes[blockSet, HoldFirst]
blockSet[LHS_ := RHS_] :=
With[{subs =
Cases[Unevaluated[LHS],
Verbatim[Pattern][p_, x_] :> Append[Hold[p], Module[{p}, p]], -2]},
{
ReplaceAll[Hold[LHS], HoldPattern[#] :> #2 & @@@ subs],
Set @@@ Hold @@ subs
} /. {Hold[newLHS_], Hold[sets__]} :> (newLHS := Block[{sets}, RHS])
]
Now:
Remove[f, a, b, x]
blockSet[
f[a_, b_] := x
]
?f
Global`f
f[a$752_, b$753_] := Block[{a = a$752, b = b$753}, x]
And:
x = a + b;
f[1, 2]
3