# How to make c[n_, x_, y_] := ToExpression["p" <> ToString[n]] = {x, y} work?

I am trying to do the following:

c[n_, x_, y_] := ToExpression["p" <> ToString[n]] = {x, y}


That is: Evaluating c[1,1,1], yields the assignment p1={1,1} but this is giving me the following error:

Set::write: Tag ToExpression in ToExpression[p2] is Protected.

I have also tried:

c[n_, x_, y_] := Evaluate[ToExpression["p" <> ToString[n]] = {x, y}]


But it also doesn't work. How can I make this work?

• Set has an attribute HoldFirst, which means you have to manually evaluate the expression to get the symbol: c[n_, x_, y_] := Set[Evaluate@Symbol["p" <> ToString[n]], {x, y}] Commented Dec 4, 2021 at 13:09
• Commented Dec 4, 2021 at 13:12
• This sounds like it might be an XY problem. For example, would c[n_,x_,y_]:=p[n]={x,y} work for your purposes? Commented Dec 4, 2021 at 13:34
• @evanb This works perfectly! Thanks! I wasn't aware I could do that. Commented Dec 4, 2021 at 14:09
• Great! Glad to help! Commented Dec 4, 2021 at 19:42

I recommend that the construct ToExpression["p" <> ToString[n]] for variable names be avoided. It makes the resulting variable names more difficult to work with. It is much more flexible to use indexed variables. You can Format the display of indexed variables in any desired manner.

ClearAll[c, p]

Format[p[n_]] := "p" <> ToString[n] (* or Subscript[p,n] *)

c[n_, x_, y_] := p[n] = {x, y}

c[#, x, y] & /@ Range[5];


The five p variables have been Set

p /@ Range[7]

(* {{x, y}, {x, y}, {x, y}, {x, y}, {x, y}, "p6", "p7"} *)


Although displayed as p6 and p7, they are stored as indexed variables

% // InputForm

(* {{x, y}, {x, y}, {x, y}, {x, y}, {x, y}, p[6], p[7]} *)


As indexed variables you can more readily work with a large number of them.

SeedRandom[1234];

Total[c[#, RandomInteger[100], RandomInteger[100]] & /@ Range[20]] ===
Total[p /@ Range[20]]

(* True *)

p[2]

(* {32, 56} *)

c[2, 6, 7] (* or p[2] = {6, 7} *)

(* {6, 7} *)

p[2]

(* {6, 7} *)


Look at:

Attributes[Set]
{HoldFirst, Protected, SequenceHold}


The attribute "HoldFirst" prohibits the evaluation of "Symbol[..]" as you can see from:

Therefore, you must force the evaluation of "Symbol" by:

Clear["p*", c]
c[n_, x_, y_] := (Evaluate[Symbol["p" <>ToString[n]]]) = {x, y}
c[1, 1, 1];
p1
(* {1, 1} *)

• Actually this only works first time. Try calling c[1, 1, 1] from new cell again and you will get an error Set::setraw: Cannot assign to raw object 1. Commented Dec 4, 2021 at 13:24