Wolfram mathematica 使用结构通过引用传递参数

Wolfram mathematica 使用结构通过引用传递参数,wolfram-mathematica,Wolfram Mathematica,这个问题是关于M中通过引用传递的主题(这里是我的一个相关问题) 当我试图找到一种不使用unevaluated[]或HoldFirst[]而通过引用传递内容的方法时,我无意中发现了这种方法,它看起来对我来说非常有效,尽管我不了解它的工作原理以及使用它的任何潜在风险。因此,我想在这里向专家们展示它,并询问他们是否认为使用它是安全的(我有一个非常大的演示,需要将参数打包成许多不同的结构来帮助管理它们,这就是我在尝试时发现这种方法的原因) 方法是这样的:首先,我们知道一个人不能写以下内容: Remove

这个问题是关于M中通过引用传递的主题(这里是我的一个相关问题)

当我试图找到一种不使用
unevaluated[]
HoldFirst[]
而通过引用传递内容的方法时,我无意中发现了这种方法,它看起来对我来说非常有效,尽管我不了解它的工作原理以及使用它的任何潜在风险。因此,我想在这里向专家们展示它,并询问他们是否认为使用它是安全的(我有一个非常大的演示,需要将参数打包成许多不同的结构来帮助管理它们,这就是我在尝试时发现这种方法的原因)

方法是这样的:首先,我们知道一个人不能写以下内容:

Remove[p]
foo[p_] := Module[{u},
   u = Table[99, {10}];
   p = u
   ];

p = 0;
foo[p];
更新上面的“p”的一种方法是更改为call to been

foo[Unevaluated@p];
或者使用
HoldFirst
定义
foo[]

但这是我发现的方法,它通过引用传递,没有以下任何一种:

我将所有参数放在一个结构中(我现在仍然这样做),并传递该结构,然后可以更新
foo[]
中结构的字段,更新将反映在函数调用的返回方式中:

Remove[parms]
foo[parms_] := Module[{u},
   u = Table[99, {10}];
   parms["p"] = u
   ];

parms["p"] = 0;
foo[parms];
现在,
parms[“p”]
包含了新的列表
{99,99,99,99,99,99}

因此,
parms
foo[]
中被覆盖/更新,而我不必告诉M通过引用传递
parms

我在我的程序中尝试了这个,我没有看到奇怪的副作用。CDF更新正常,没有错误。我不知道这是如何工作的,可能是M将本例中
parms
内的所有字段都视为全局字段

但无论是哪种情况,我都很满意,因为它为我提供了一种将许多参数打包到结构中的方法,同时我能够在函数内部对结构进行更新

但我的问题是:你认为这种方法存在重大问题吗?它是如何在内部工作的?我的意思是,如果我没有执行
HoldFirst
Unevaluated
,M如何处理这个传球?我知道我现在失去了像以前一样进行参数检查的能力,但我不能拥有我想要的一切。正如我前面所说,M需要一个真正的内置结构作为语言的一部分并集成到其中。但这是另一次讨论

顺便说一句,到目前为止,我看到的最好的结构仿真是由Leonid Shifrin在本文末尾发布的,但不幸的是,我无法在演示中使用它,因为它使用了演示CDF中不允许的符号

谢谢

更新: 顺便说一句,下面是我认为M是如何处理这个问题的。这只是我的猜测:我认为
param
是一种查找表,它的字段只是用作它的索引(可能是哈希表?),然后
param[“p1”]
将包含
param[“p1”]
的实际值所在的堆中某个位置的地址(而不是值)

大概是这样的:

因此,当传递
param
时,然后在函数
foo[]
内,当键入
param[“p1”]=u
时,它将导致
“p1”
指向的当前内存被释放,然后从堆中分配一个新内存,并在其中复制
u
的值

因此,返回时,这就是为什么我们看到
param[“p1”]
的内容发生了变化。但实际上发生了变化的是
param[“p1”]
表示的地址指向的内存内容。(有一个名称,它是
“p1”
,还有一个地址字段,它指向名称
“p1”
表示的内容)

但是,这意味着地址本身,即
“p1”
所代表的地址已经更改,但查找名称本身(即“p1”)没有更改

因此,由于名称本身没有更改,因此不需要使用HoldFirst,即使该名称所代表的对象所指向的数据已被修改

更新:

这个方法有一个小毛病,但解决它并不是什么大问题:当使用这个索引对象方法传递东西时,结果是不能修改对象的一部分。例如,以下操作不起作用:

foo[param_] := Module[{},
   param["u"][[3]] = 99 (*trying to update PART of u *)
   ];

param["u"] = Table[0, {5}];
foo[param];
上面给出了错误

Set::setps: "param[u] in the part assignment is not a symbol"
但解决办法很简单。为要更新部分字段的整个字段创建一个本地副本,然后更新(部分)本地副本,然后将副本写回字段,如下所示

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];
嗯。如果可以更新部分字段,则更好,因此不需要“特殊”处理。但至少工作环境并没有那么糟糕

更新 为了完整起见,我想我还提到了另一件关于使用索引对象和解决方法的小事

我写

param[u] = {1, 2, 3}
param[u][[1 ;; -1]]
它按预期返回
{1,2,3}

然后我发现我可以使用
param@u
而不是
param[u]
,这真的很有帮助,因为太多的实心括号开始让我头疼

但是当我打字的时候

param@u[[1 ;; -1]]
希望能得到和以前一样的答案,它没有。有一个错误,(我想我知道为什么,运算符优先级问题,但现在不是重点),只是想说解决方法很简单,可以使用
param[u][[1;;-1]]
或使用
(param@u)[[1;;-1]]

我喜欢
(param@u)[[1;;-1]]
more,这就是我现在用来访问索引对象中的列表的方法


符号
param@u
是我能达到的标准记录符号,即
param.u
,因此我现在对索引对象很满意,而且它似乎工作得很好。只有几件小事需要注意。

我不打算回答低级数据结构的问题,因为我根本没有资格这样做

您正在创建
Remove[parms, foo]

foo[thing_] :=
  Module[{u},
    u = Table[99, {10}];
    thing["p"] = u
  ];

parms["p"] = 0;
foo[parms];

parms["p"]

DownValues[parms]
{HoldPattern[parms["p"]] :> {99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}
With[{thing = parms}, thing[x]]