Let's start by defining a helper function which will assemble the individual function definitions:
makeAssignment[wrapper_][e_, {i_}] :=
( ToExpression["R"~~ToString[i], InputForm, Hold]
/. Hold[s_] :> Hold[s[x_, y_], e]
/. Hold[l_, r_] :> wrapper[SetDelayed[l, r]]
)
Since we wish to prevent the definitions from evaluating immediately, we must wrap the expression in something that will prevent such evaluation. makeAssignment
is defined in a way that allows us to try out different wrappers.
For example, we can simply Hold
the result:
makeAssignment[Hold][x**y, {5}]
(* Hold[R5[x_, y_] := x ** y] *)
Or, we can use Defer
to prevent the evaluation but generate a final top-level output that uses no wrapper:
makeAssignment[Defer][x**y, {5}]
(* R5[x_, y_] := x ** y *)
Or, we can use HoldForm
which will give a result that looks like it has no wrapper but in fact has an invisible one:
makeAssignment[HoldForm][x**y, {5}]
(* R5[x_, y_] := x ** y *)
Head[%]
(* HoldForm *)
We can even use Identity
if we do not want to hold the evaluation at all and actually install the definition:
makeAssignment[Identity][x**y, {5}]
(* Null *)
??R5
(* R5[x_,y_] := x**y *)
We can now use this helper function to produce a desired list using various strategies of evaluation control.
Given:
$vs = { x, y, inv[x], inv[y], x ** y, 1, x ** inv[y], x ** x
, y ** inv[x], y ** x, y ** y, inv[x] ** inv[y], inv[x] ** y
, inv[x] ** inv[x], inv[y] ** x, inv[y] ** inv[x], inv[y] ** inv[y]
};
We can get a list of deferred expressions...
$vs // MapIndexed[makeAssignment[Defer]]
(* {R1[x_, y_] := x, R2[x_, y_] := y, R3[x_, y_] := inv[x], ... } *)
... or a list of held expressions ...
$vs // MapIndexed[makeAssignment[Hold]]
(* {Hold[R1[x_, y_] := x], Hold[R2[x_, y_] := y], Hold[R3[x_, y_] := inv[x]], ... } *)
... or a single held sequence ...
$vs // MapIndexed[makeAssignment[Hold]] // Apply[Hold] // Flatten
(* Hold[R1[x_, y_] := x, R2[x_, y_] := y, R3[x_, y_] := inv[x], ... ] *)
... as suits our fancy.
Notes
There are some subtleties in the definition of makeAssignment
in order to generate the definitions we desire.
Tailored argument list form
We could have defined makeAssignment
like this:
makeAssignment[wrapper_, e_, i_] := ...
instead of what we did:
makeAssignment[wrapper_][e_, {i_}] := ...
But by making wrapper
a curried argument and using {i_}
we made the function more convenient to use with the operator form of MapIndexed
, i.e.:
... // MapIndexed[makeAssignment[Hold]]
vs.
... // MapIndexed[makeAssignment[Hold, #, #2[[1]]]&, #]&
Ever since version 10 dramatically increased the usage of operator forms, such syntactical niceties have become more commonplace.
ToExpression vs. Symbol
makeAssignment
uses ToExpression[..., Hold]
instead of Symbol
to create the function names in order to ensure that we obtain an unevaluated symbol and not its value. Consider:
XYZ10 = 666;
Symbol["XYZ10"]
(* 666 *)
ToExpression["XYZ10", InputForm, Hold]
(* Hold[XYZ10] *)
Double Replacement
makeAssignment
uses two replacement expressions (/.
) in order to construct the function definition expression. It does this to avoid some unwelcome symbol renaming that occurs when the evaluator get defensive about the scope of the free variables x
and y
. Consider:
Hold[V1, x + y] /. Hold[s_, e_] :> Hold[s[x_, y_] := e]
(* Hold[V1[x$_, y$_] := x + y] *)
Hold[V1, x + y] /. Hold[s_, e_] :> Hold[s[x_, y_], e] /. Hold[l_, r_] :> Hold[l := r]
(* Hold[V1[x_, y_] := x + y] *)
There are other strategies to deal with this, but they are all as messy as this or messier. In fact, dealing with unevaluated expressions in Mathematica is generally a very messy business prone to evaluation leaks.
(I'm starting a pool on how long it takes a reader of this post to find an evaluation leak :)
Hold vs. HoldComplete
This post uses Hold
liberally to prevent evaluation. A more defensive implementation might use HoldComplete
instead. The differences are minor, but it is really a design choice to prefer one over the other. For example, HoldComplete
prevents the action of up-values. Depending upon the wider application this might be desirable or undesirable. In a code generation context, up-values can be very useful for implementing special rules when transforming held expressions. On the other hand, they are a potential source of evaluation leaks and other surprising behaviours. The designer must choose. The use of Hold
is far more common than that of HoldComplete
, but this is not necessarily a conclusive argument.
Hold
, to avoid evaluation? Or you want to automatically define these functions? $\endgroup$R1[x_,y_]:=x
cannot be the output because it immediately evaluates.Hold[R1[x_,y_]:=x]
can be. Or do you just want to write a program that evaluatesR1[x_,y_]:=x
(not outputs it as a result)? $\endgroup$R1[x_,y_]:=x
is not a function. It is a function definition. The function itself isR1
. The definition cannot be returned unevaluated unless you wrap it byHold
. The function name can. $\endgroup$