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

Nas*_*ser 7 wolfram-mathematica

这个问题是关于在M中通过引用的主题(我的相关问题是关于在函数之间传递数据的简单问题)

虽然我试图找到一种方法来通过引用传递东西而不使用Unevaluted[]或者HoldFirst[],但是我错误地使用了这种方法并且看起来对我来说效果很好,即使我不理解它是如何工作的以及使用它的任何隐藏风险.所以我想在这里向专家展示,并询问他们是否认为使用是安全的(我有非常大的演示,需要将参数打包成不同结构的数量来帮助管理它们,这就是我发现这个的方法我正在尝试的方法).

这是方法:首先我们知道不能写下面的内容:

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

p = 0;
foo[p];
Run Code Online (Sandbox Code Playgroud)

在上面更新"p"的一种方法是更改​​为调用成为

foo[Unevaluated@p];
Run Code Online (Sandbox Code Playgroud)

或者通过定义foo[]HoldFirst.

但这是我发现的方式,它通过引用传递,没有这些:

我将所有参数放在一个结构中(我现在无论如何都这样做),并传递结构,然后可以更新结构内部的字段,foo[]更新将反映在函数调用的后面:

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

parms["p"] = 0;
foo[parms];
Run Code Online (Sandbox Code Playgroud)

现在,parms["p"]包含新列表{99, 99, 99, 99, 99, 99, 99, 99, 99, 99}

所以,parms内部被覆盖/更新foo[],我不必告诉M parms通过引用传递!

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

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

但我的问题是:你看到这种方法存在重大问题吗?它在内部如何运作?我的意思是如何处理中号这逝去的没有我做的HoldFirst还是Unevaluated?我知道我现在失去了像以前一样进行参数检查的能力,但我不能拥有我想要的一切.正如我之前所说,M需要一个真正的内置结构作为语言的一部分并集成到其中.但这是另一个时间来谈论.

顺便说一句,最好的结构仿真我看到到目前为止是由这一个列昂尼德·希夫林张贴在该线程的结束位置,但不幸的是,因为它使用的演示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];
Run Code Online (Sandbox Code Playgroud)

以上给出了错误

Set::setps: "param[u] in the part assignment is not a symbol"
Run Code Online (Sandbox Code Playgroud)

但解决方法很简单.制作一个想要更新其中一部分的整个字段的本地副本,然后更新(部分)本地副本,然后将副本写回字段,就像这样

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];
Run Code Online (Sandbox Code Playgroud)

好.如果可以更新部分字段会更好,因此不需要"特殊"处理.但至少周围的工作并不是那么糟糕.

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

我写

param[u] = {1, 2, 3}
param[u][[1 ;; -1]]
Run Code Online (Sandbox Code Playgroud)

哪个{1,2,3}按预期返回.

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

但是当我打字的时候

param@u[[1 ;; -1]]
Run Code Online (Sandbox Code Playgroud)

期望得到与以前相同的答案,但事实并非如此.一个人得到一个错误,(我想我知道为什么,运营商优先问题,但现在不是重点),只是想说解决方法很简单,无论是使用 param[u][[1 ;; -1]] 还是使用(param@u)[[1 ;; -1]]

(param@u)[[1 ;; -1]]更喜欢,所以这就是我现在用来访问索引对象内的列表.

符号param@u很接近我可以达到标准记录符号,param.u所以我现在对索引对象感到高兴,它似乎工作得很好.只需要注意几件小事.

Mr.*_*ard 5

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

您正在创建"索引对象"并使用不可变字符串作为索引.一般来说,我认为这些类似于哈希表.

以您的代码示例为例,让我们通过使用以下参数的唯一名称来消除可能的歧义foo:

Remove[parms, foo]

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

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

parms["p"]

DownValues[parms]
Run Code Online (Sandbox Code Playgroud)
{HoldPattern[parms["p"]] :> {99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}

这显示了如何根据高级Mathematica结构存储数据.

parms是一个全球性的象征.正如您所观察到的那样,它可以安全地"传递"而不会发生任何事情,因为它只是一个没有符号,OwnValues并且在评估时不会触发任何内容.这与单独编写/评估完全相同foo.

发生的替换foo[thing_] := ...类似于With.如果我们写:

With[{thing = parms}, thing[x]]
Run Code Online (Sandbox Code Playgroud)

thingin 在评估之前thing[x]被替换.同样,在上面的代码中,我们得到之前或评估.那时,接管的属性,你得到你想要的.parmsthing[x]parms["p"] = uthing["p"]SetHoldFirstSet

由于您使用不可变字符串作为索引,因此不存在更改的危险.只要你知道如何处理非本地化符号,parms那么我在使用这种方法时也没有新的危险.