Why inserting a command into a procedure yields error

I have a square matrix m to which I want to assign new values into its diagonal. For example,

m={
  {20, 24, 33, 36, 34},
  {39, 13, 15, 30, 30},
  {18, 17, 16, 28, 27},
  {39, 35, 33, 32, 37},
  {30, 21, 22, 10, 23}
}

I want to change the diagonal elements into some negative numbers:

Table[
 m[[i, i]] = RandomInteger[-10, Length[m]]〚i〛,
 {i, 1, Length[m]}]

That works just fine.

Now I want to write a function f1 that performs this manipulation, and this is where I got stuck:

f1[q_] := Table[
  q[[i, i]] = RandomInteger[{-10, 0}, Length[q]]〚i〛,
  {i, 1, Length[q]}]

f1[m]

gives

Set::setps: {{20,24,33,36,34},{39,13,15,30,30},{18,17,16,28,27},{39,35,33,32,37},{30,21,22,10,23}} in the part assignment is not a symbol.

Set::setps: {{20,24,33,36,34},{39,13,15,30,30},{18,17,16,28,27},{39,35,33,32,37},{30,21,22,10,23}} in the part assignment is not a symbol.

Set::setps: {{20,24,33,36,34},{39,13,15,30,30},{18,17,16,28,27},{39,35,33,32,37},{30,21,22,10,23}} in the part assignment is not a symbol.

General::stop: Further output of Set::setps will be suppressed during this calculation.

Why is that?

Try to wrap Unevaluated around the argument when calling f1

f1[Unevaluated[m]]

Lo and behold. Works.

Could you explain why?

I will try to give a condensed explanation.

By default a function argument is evaluated before it is passed on to the functions body. So the argument q becomes an explicit 5x5 matrix when it is seen as the left hand side of the assignment in Table.

And the assignment lhs[[x]]=rhs works only if lhs is a symbol. If not it gives the error you saw.

Unevaluated prevents evaluation of the argument. So q becomes the symbol m.

Another way

SetAttributes[f1, HoldFirst]

Yes Rohit, Unevaluated and the function attribute HoldFirst gives the same effect in this case. But one could argue that Unevaluated is more ‘entry level’ and easier to explain.

Hi Ehud

What you are trying is called side effect programming which is considered as bad style and should be generally avoided in any programming. In partikular you are trying to pass a parameter by reference which is not the “Mathematica Way” .

It is better to generate a new object.

ReplacePart[m, {{i_, i_}} :> RandomInteger[-10]]

m=ReplacePart[m, {{i_, i_}} :> RandomInteger[-10]]

f1[q_] := ReplacePart[q, {{i_, i_}} :> RandomInteger[-10]]
m = f1[m]

Totally agree with you. Thanks a lot for pointing that out and providing this nice solution.