9

簡単な質問ですが、はるかに効率的な明らかな解決策を見落としていないことを確認するために質問しています。

非常に大きなリストなどの大きなデータ バッファーがあり、それを更新する必要があり、それを関数に渡して関数内で更新を行いたい場合は、次のようにします。

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

また、参照渡しを使用できないため (CDF では、関数の属性を HoldFirst などの何かに変更することはできません)、関数自体の内部でリストのコピーを順番に作成する必要があります。それを更新し、コピーを返します。

私の質問は、良くない「グローバル変数」を使用する以外に、これを行うためのより効率的な方法はありますか?

ps。約 1 年前、参照によるコピーについて 質問しました。Mathgroup の質問へのリンクを次に示します。(レオニードの回答のおかげで、有用な回答でした)。

しかし、ここでの私の質問は少し異なります.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 コードをモジュール化したいので、処理する大きなデータを渡す関数を作成する必要がありますが、同時に「効率的」に保ちたいと考えていました。

これを行うために見ることができる他のオプションはありますか?

これが以前に尋ねられた場合は申し訳ありませんが、SOを検索するのは難しいです。

ありがとう、

追加1

Unevaluated を使用するのは簡単ですが、リストが渡されていることを確認するために必要だった型チェックを使用できなくなりました。例えば

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

「a」にはヘッダー List がないため、この呼び出しは定義に「バインド」しません。

そのため、コードの堅牢性の一部が失われます。しかし、Unevaluated を使用すると CDF で機能し、それを使用するようにコードを変更するのは簡単でした。機能させるためにそこにあった余分な「型チェック」を削除する必要がありました。

4

2 に答える 2

16

この関数Unevaluatedは、(一時的に) 属性を設定するのとほぼ同じ効果があるHoldFirstため、次のようなことができます。

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

編集

追加1について:次のようなことで型チェックを追加できます

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

それで

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

以前と同じように動作しますが、

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

最後のステートメントを未評価の形式で返すだけです。

于 2011-09-11T08:38:16.683 に答える
12

または、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}

編集

完全を期すために、ここに別の方法があります。これはあまりエレガントではありませんが、特にいくつかのパラメーターがあり、複数のパラメーターを保持したい場合に時々使用していることに気付きました (しかし、そのような、HoldFirstまたはHoldRest十分ではないなどのたとえば、1 番目と 3 番目): 次のように、パラメーターを でラップ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}

編集2

主な関心事がカプセル化である場合は、次のようModuleに、永続的なローカル変数とメソッドにアクセスして変更するために使用することもできます。

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

構造的な観点からはまだ (ほぼ) グローバル変数ですが、他の変数と名前が競合する危険はありません。ミューテーター メソッドを使用することによってのみ行うことができるため、変更された場所を追跡する方が簡単です。上記(直接ではありません)。次のように使用します。

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

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

これは、Java で単純化された JavaBean を作成するようなものです。つまり、変更可能なデータのコンテナー (状態をカプセル化する方法) です。追加のメソッド呼び出し (ホールド属性またはUnevaluated- ベースのメソッド) により、わずかなオーバーヘッドが発生します。多くの場合、それは必要ありませんが、場合によっては、そのように状態をカプセル化したい場合があります。 (ステートフルな) コードを簡単にテストできます。個人的には、UI プログラミングと、データベースとのインターフェイスに関連するコードで、これを数回実行しました。

同じ精神で、関数間でいくつかの変数を共有して、それらの関数をModuleスコープ内で定義することもできます。この場合、getter メソッドと setter メソッドは必要ないかもしれません。共有状態を持つそのようなグローバル関数はclosures. これについては、このMathGroup スレッドの 3 番目の投稿で、より詳細な議論を見つけることができます。

于 2011-09-11T09:41:55.280 に答える