This question is about the transfer by reference in M โโ(the related question here is the simple question of transferring data between functions )
While I was trying to find a way to pass things by reference without using Unevaluted[]
or HoldFirst[]
, I was wrong about this method and it looks really good to me, although I donโt understand how this works and any hidden risks of using it . Therefore, I would like to show it here to experts and ask if they think it is safe to use it (I have a very large demonstration, and I need to pack the parameters into a number of different structures to help them deal with, and here is how I found this method, for now I tried to do something).
Here is the method: first we know that you cannot write the following:
Remove[p] foo[p_] := Module[{u}, u = Table[99, {10}]; p = u ]; p = 0; foo[p];
One way to update "p" in the above case is to change to a call to become
foo[ Unevaluated@p ];
Or by defining foo[]
using HoldFirst
.
But this is how I found what makes the link pass without any of them:
I put all the parameters in the structure (I do it anyway now) and pass the structure, and then you can update the structure fields inside foo[]
, and the updates will be reflected in the return path from the function call:
Remove[parms] foo[parms_] := Module[{u}, u = Table[99, {10}]; parms["p"] = u ]; parms["p"] = 0; foo[parms];
Now parms["p"]
contains a new list {99, 99, 99, 99, 99, 99, 99, 99, 99, 99}
So, parms
been rewritten / updated inside foo[]
without having to specify M to pass parms
by reference!
I tried this in my program and I don't see any weird side effects. CDF was updated without errors. I donโt know how this works, maybe M treats all fields inside parms
in this example as global?
But in any case, I am satisfied with this, since it provides me with a way to pack my many parameters into structures and at the same time I can perform an update inside the functions for the structure.
But my question is: do you see the main problems with this method? How does it work domestically? I mean, how does M handle this walkthrough without me doing HoldFirst
or Unevaluated
? I know that now I have lost the ability to check parameters as before, but I canโt have everything that I want. As I said, M needs a real built-in structure as part of the language and integrated into it. But talk about this at another time.
Btw, the best structural emulation I've seen so far was this Leonid Shifrin, published at the end of this thread here but, unfortunately, I could not use it in my demo, as it uses characters that are not allowed in the demo CDF.
thanks
Update: By the way, this is below, which I think is how M handles this. This is just my guess: I think that param
is some kind of lookup table, and its fields are simply used as an index in it (maybe a hash table?). Then param["p1"]
will contain the address (not the value) of the location on the heap where the real param["p1"]
value is located.
Something like that:
So, when passing param
, then inside the function foo[]
when you enter param["p1"]=u
this will free up the current memory pointed to by "p1"
, and then the new memory allocated from the heap, and the value is copied into it u
.
So, let's go back, so we see that the contents of param["p1"]
changed. But what was actually changed is the contents of the memory pointed to by the address that param["p1"]
represents. (there is a name "p1"
and an address field that points to the content that represents the name "p1"
)
But this means that the address itself, which "p1"
represents, has changed, but the search name itself (which is "p1") has not changed.
So, since the name itself has not changed, did you not have to use HoldFirst, even if the data pointed to by that name has been changed?
Update:
There is one small glitch in this method, but you should not work with it: when transferring things using this method of indexed objects, it turns out that you cannot change part of the object. For example, this does not work below:
foo[param_] := Module[{}, param["u"][[3]] = 99 (*trying to update PART of u *) ]; param["u"] = Table[0, {5}]; foo[param];
Above error
Set::setps: "param[u] in the part assignment is not a symbol"
But the workaround is simple. make a local copy of the whole field you want to update, and then update (part) of the local copy, and then return the copy to the field, for example,
foo[param_] := Module[{u = param["u"]}, (* copy the whole field *) u[[3]] = 99; (*update local copy *) param["u"] = u (*now update the field, ok *) ]; param["u"] = Table[0, {5}]; foo[param];
Well. It would be better if part of the field could be updated, so no "special" processing would be required. But at least the work around is not so bad.
Update For completeness, I thought I was mentioning one more tiny thing about using indexed objects and about working.
I wrote
param[u] = {1, 2, 3} param[u][[1 ;; -1]]
Returns {1,2,3}
as expected.
Then I found that instead of param[u]
I can use param@u
, which really helped, because too many solid brackets start to give me a headache.
But then when I typed
param@u [[1 ;; -1]]
awaiting an answer to the same answer as before, he did not. an error occurs, (I think I know why the problem is with the operator priority, but now itโs not), I just wanted to say that the workaround is simple, or you can use param[u][[1 ;; -1]]
param[u][[1 ;; -1]]
, or use ( param@u )[[1 ;; -1]]
( param@u )[[1 ;; -1]]
I like it better (pa ram@u )[[1 ;; -1]]
(pa ram@u )[[1 ;; -1]]
, so I now use access to lists inside indexed objects.
The param@u
notation is so close that I can get the standard notation of the param.u
, so now I'm happy with indexed objects now and it seems to work very well. Just a couple of minor things to keep in mind.