4
$\begingroup$

I have a list of that contains words of two non commutative variables. Something like :

{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]}

I would like to take this list as an input to a function a return a list of unevaluated functions of the two variables. In other words the output should be something like :

{R1[x_,y_]:=x,R2[x_,y_]:=y,R3[x_,y_]:=inv[y],R4[x_,y_]=x ** y,...}

I am sure I am not using the right script here but I want to convey the idea that I want to return a sequence of functions that I am going to evaluate later in another function.

$\endgroup$
5
  • $\begingroup$ You mean you want a list of function definitions, each wrapped in Hold, to avoid evaluation? Or you want to automatically define these functions? $\endgroup$
    – Szabolcs
    Commented Dec 4, 2016 at 10:06
  • $\begingroup$ The definition is given in the list entered (an example is the first list above). So, I guess this fits in the first part of your question. $\endgroup$
    – Steve
    Commented Dec 4, 2016 at 10:21
  • $\begingroup$ Sorry, I still don't understand. Can you give an example of the actual output you want? 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 evaluates R1[x_,y_]:=x (not outputs it as a result)? $\endgroup$
    – Szabolcs
    Commented Dec 4, 2016 at 10:42
  • $\begingroup$ I am sorry. Here is an example : R1[x+y,z]=x+y. Another example R2[xy,a]=xy. I do not want the function to return the evaluation. I want it to return the function itself (in the simple case). In the more complicated case, I would like to return a list of such functions. Each one of them is also unevaluated. $\endgroup$
    – Steve
    Commented Dec 4, 2016 at 11:11
  • 1
    $\begingroup$ R1[x_,y_]:=x is not a function. It is a function definition. The function itself is R1. The definition cannot be returned unevaluated unless you wrap it by Hold. The function name can. $\endgroup$
    – Szabolcs
    Commented Dec 4, 2016 at 12:19

2 Answers 2

5
$\begingroup$

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.

$\endgroup$
1
  • $\begingroup$ Thank you for your detailed answer. Much appreciated. $\endgroup$
    – Steve
    Commented Dec 4, 2016 at 18:19
1
$\begingroup$

Not sure what you really want to do, but happy to see you seem to be using NCAlgebra :)

As for your question, why not simply:

rhs = {x, y, inv[x], inv[y], x ** y}
lhs = {R1, R2, R3, R4, R5}
fun = MapThread[(Hold[#1[x_, y_] := #2]) &, {lhs, rhs}]

When you want to create the functions in fun:

ReleaseHold /@ fun

If you just want to create them right away just ommit Hold as in:

fun = MapThread[(#1[x_, y_] := #2) &, {lhs, rhs}]
$\endgroup$

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.