关于函数之间传递数据的简单问题

发布于 2024-12-03 17:18:20 字数 1806 浏览 4 评论 0原文

简单的问题,但我问只是为了确保我没有忽略一个明显的问题 解决方案可以更加有效。

如果有很大的数据缓冲区,例如非常大的列表,则需要更新,并且 想要将它传递给一个函数以在函数内部进行更新

a = Table[0,{10}]
a = update[a]

,因为我不能使用引用传递(在 CDF 中,无法更改 函数的属性可以是任何东西,例如HoldFirst),那么我就被迫 在函数本身内部创建列表的副本以便更新它, 并归还副本。

我的问题是,除了使用不好的“全局变量”之外, 有没有更有效的方法来做到这一点?

附:大约一年前,我询问了有关引用复制的问题,这是一个链接 我的数学组问题。 (顺便说一句,感谢 Leonid 的回答,这是有用的答案)。

但我的问题有点不同,因为现在我不能使用 HoldFirst, 我没有看到任何其他替代方案来避免这种额外的情况 一直在复制数据,这似乎会减慢程序的速度 尺寸变得太大。

(不能使用 SetAttributes 及其朋友,CDF 中不允许)。

我将首先展示基本示例,然后展示如果 我可以使用 HoldFirst。

示例

update[a_List] := Module[{copyOfa = a}, copyOfa[[1]] = 5; copyOfa]
a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

如果我可以使用 HoldFirst,我的编写

update[a_] := Module[{}, a[[1]] = 5; a]
Attributes[update] = {HoldFirst};

a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

效率会更高,因为无需进行复制。通过引用传递。

我可以使用全局变量,如

a = Table[0, {10}];
updateMya[] := Module[{}, a[[1]] = 5]
updateMya[];
a
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

但是这当然是糟糕的编程,即使它非常快。

由于我有大量数据缓冲区,并且我想模块化我的 Mathematica 代码, 我需要创建函数来传递大数据来处理,但同时 时间想要保持“高效”。

人们还可以看到其他选择来做到这一点吗?

抱歉,如果之前有人问过这个问题,那么很难搜索。

谢谢,

addition 1

使用 Unevaluated 很容易使用,但我无法再使用类型检查来确保列表被传递。例如,

update[a_List] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

调用 now 不会“绑定”到定义,因为“a”现在没有标头列表。

因此,我失去了代码中的一些稳健性。但使用 Unevaluated 确实可以在 CDF 中工作,并且更改代码以使用它很容易。我只需要删除那些额外的“类型检查”即可使其正常工作。

Simple question, but I am asking just to make sure I am not overlooking an obvious
solution which can be much more efficient.

If one has large data buffer, say very large list, that needs to be updated, and
wanted to pass it to a function to do the updating inside the function as in

a = Table[0,{10}]
a = update[a]

and since I can't use pass by reference (in a CDF, one can't change the
Atrributes of a function to anything, such as HoldFirst), then I am forced
to make a copy of the list inside the function itself in order to update it,
and return back the copy.

My question, other than using 'global variables' which is not good,
is there a more efficient way to do this?

ps. about a year ago, I asked about copy by reference, here is a link
to my Mathgroup question. (Thanks to Leonid answer there btw, was useful answer).

But my question here is a little different, since now I can NOT use HoldFirst,
are there any other alternatives that I am not seeing to avoid this extra
copying of data all the time, it seems to slow down the program when
the size becomes too large.

(can't use SetAttributes and its friends, not allowed in CDF).

I'll show the basic example first, then show how I would do it if
I could use the HoldFirst.

Example

update[a_List] := Module[{copyOfa = a}, copyOfa[[1]] = 5; copyOfa]
a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

If I could use the HoldFirst, I would write

update[a_] := Module[{}, a[[1]] = 5; a]
Attributes[update] = {HoldFirst};

a = Table[0, {10}];
a = update[a]

----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Much more efficient, since no copying is done. Pass by reference.

I could use a global variable, as in

a = Table[0, {10}];
updateMya[] := Module[{}, a[[1]] = 5]
updateMya[];
a
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

But this is of course bad programming even if is it very fast.

Since I have large data buffers, and I'd like to modularize my Mathematica code,
I need to create functions that I pass it large data to process, but at the same
time wanted to keep it 'efficient'.

Any other options one can see to do this?

sorry if this was asked before here, hard to search SO.

thanks,

addition 1

Using Unevaluated is easy to use, but I am no longer able to use the type checking I had to make sure that a list is being passed. For example

update[a_List] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

The call now does not 'bind' to the definition, since 'a' now does not have header List.

So, I lose some of the robustness I had in the code. But using Unevaluated does work in CDF and changing the code to use it was easy. I just had to remove those extra 'type checking' that I had there to make it work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

咋地 2024-12-10 17:18:20

函数Unevaluated与(临时)设置属性HoldFirst的效果几乎相同,因此您可以执行

update[a_] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

Edit

之类的操作关于添加1:您可以通过执行类似

Clear[update];
update[a_] := Module[{}, a[[1]] = 5; a] /; Head[a] == List

Then 的

a = Table[0, {10}];
update[Unevaluated[a]]

操作来添加类型检查,就像以前一样工作,但

b = f[1,2,3];
update[Unevaluated[b]]

只以未计算的形式返回最后一个语句。

The function Unevaluated has pretty much the same effect as (temporarily) setting the Attribute HoldFirst so you could do something like

update[a_] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

Edit

Concerning addition 1: you could add type checking by doing something like

Clear[update];
update[a_] := Module[{}, a[[1]] = 5; a] /; Head[a] == List

Then

a = Table[0, {10}];
update[Unevaluated[a]]

works as before but

b = f[1,2,3];
update[Unevaluated[b]]

just returns the last statement in unevaluated form.

不必在意 2024-12-10 17:18:20

或者,如果 CDF 允许,您可以使用带有 Hold* 属性的纯函数,如下所示:

update = Function[a, a[[1]] = 5; a, HoldFirst]

然后,像平常一样使用它:

In[1408]:= 
a=Table[0,{10}];
update[a];
a

Out[1410]= {5,0,0,0,0,0,0,0,0,0}

编辑

为了完整起见,这里有另一种方法,不太优雅,但我发现自己有时会使用它,特别是当您有多个参数并且想要保存多个参数时(但这样 HoldFirstHoldRest 是不够好,比如第一和第三,例如):只需将参数包装在 Hold 中,并将其记录在函数的签名中,如下所示:

updateHeld[Hold[sym_], value_] := (sym[[1]] = value; sym)

您将其用作:

In[1420]:= a=Table[0,{10}];
updateHeld[Hold[a],10];
a

Out[1422]= {10,0,0,0,0,0,0,0,0,0}

EDIT 2

如果您主要关心的是封装,您还可以使用 Module 创建持久性局部变量以及访问和修改它的方法,如下所示:

Module[{a},
   updateA[partIndices__, value_] := a[[partIndices]] = value;
   setA[value_] := a = value;
   getA[] := a
]

从结构的角度来看,它仍然(几乎)是一个全局变量,但不存在危险与其他变量的名称冲突,而且更容易跟踪它的更改位置,因为您只能使用上面的 mutator 方法(但不能直接)来做到这一点。您可以将其用作:

In[1444]:= 
setA[Table[0,{10}]];
updateA[1,5];
getA[]

Out[1446]= {5,0,0,0,0,0,0,0,0,0}

这就像用 Java 制作一个简单的 JavaBean - 一个可变数据的容器(一种封装状态的方法)。由于额外的方法调用(基于 Hold-attribute 或 Unevaluated 的方法),您将有轻微的开销,并且在许多情况下您不需要它,但在某些情况下您可能想要封装像这样的状态 - 它可能会让你的(有状态的)代码更容易测试。就我个人而言,我已经在 UI 编程和与数据库接口相关的代码中这样做过几次。

本着同样的精神,您还可以在函数之间共享一些变量,在 Module 范围内定义这些函数 - 在这种情况下,您可能不需要 getter 和 setter 方法,并且此类具有共享状态的全局函数是闭包。您可以在我的第三篇文章 MathGroup 线程。

Alternatively, and if CDF allows that, you can use a pure function with a Hold*-attribute, like so:

update = Function[a, a[[1]] = 5; a, HoldFirst]

Then, you use it as usual:

In[1408]:= 
a=Table[0,{10}];
update[a];
a

Out[1410]= {5,0,0,0,0,0,0,0,0,0}

EDIT

Just for completeness, here is another way, which is less elegant, but which I found myself using from time to time, especially when you have several parameters and want to hold more than one (but such that HoldFirst or HoldRest are not good enough, such as first and third, for example): just wrap your parameter in Hold, and document it in the function's signature, like this:

updateHeld[Hold[sym_], value_] := (sym[[1]] = value; sym)

You use it as:

In[1420]:= a=Table[0,{10}];
updateHeld[Hold[a],10];
a

Out[1422]= {10,0,0,0,0,0,0,0,0,0}

EDIT 2

If your main concern is encapsulation, you can also use Module to create persistent local variable and methods to access and modify it, like so:

Module[{a},
   updateA[partIndices__, value_] := a[[partIndices]] = value;
   setA[value_] := a = value;
   getA[] := a
]

It is still (alomost) a global variable from the structural point of view, but there is no danger of name collisions with other variables, and it is easier to track where it is changed, since you only can do it by using the mutator methods above (but not directly). You use it as:

In[1444]:= 
setA[Table[0,{10}]];
updateA[1,5];
getA[]

Out[1446]= {5,0,0,0,0,0,0,0,0,0}

This is like making a simplistic JavaBean in Java - a container for mutable data (a way to encapsulate the state). You will have a slight overhead due to extra method invocations (w.r.t. Hold-attribute or Unevaluated - based methods), and in many cases you don't need it, but in some cases you may want to encapsulate the state like that - it may make your (stateful) code easier to test. Personally, I've done this a few times for UI-programming and in the code related to interfacing with a database.

In the same spirit, you can also share some variables between functions, defining those functions inside the Module scope - in this case, you may not need getter and setter methods, and such global functions with shared state are closures. You may find much more detailed discussion of this in my third post in this MathGroup thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文