MathematicaのForEachループ
-
03-07-2019 - |
質問
次のようなものが欲しい:
each[i_, {1,2,3},
Print[i]
]
または、より一般的には、次のように、ループしているリスト内の任意のものを分解します:
each[{i_, j_}, {{1,10}, {2,20}, {3,30}},
Print[i*j]
]
通常は、 Map
または他の純粋に機能的な構成を使用し、副作用を使用する非機能的なプログラミングスタイルを避けます。しかし、for-eachコンストラクトが非常に役立つと思う例があります:
次のように、シンボルと式をペアにするオプション(ルール)のリストがあるとします
attrVals = {a -> 7, b -> 8, c -> 9}
次に、これらのシンボルをそれらの番号に明確にマッピングするハッシュテーブルを作成します。私はそれを行うよりクリーンな方法があるとは思わない
each[a_ -> v_, attrVals, h[a] = v]
追加のテストケース
この例では、変数のリストを変換します:
a = 1;
b = 2;
c = 3;
each[i_, {a,b,c}, i = f[i]]
上記の後、 {a、b、c}
は {f [1]、f [2]、f [3]}
と評価されるはずです。これは、リストの場合、 each
の2番目の引数を評価せずに保持する必要があることに注意してください。
未評価のフォームがリストでない場合、2番目の引数を評価する必要があります。例:
each[i_, Rest[{a,b,c}], Print[i]]
b
および c
の値を印刷する必要があります。
補遺:for-eachを適切に行うには、 Break []
および Continue []
をサポートする必要があります。それを実装する方法がわかりません。 Break []
および Continue []
をサポートする唯一のループ構造であるため、For、While、またはDoの観点から何らかの方法で実装する必要があるでしょう。
そしてこれまでの答えに関する別の問題:彼らは Return []
を食べます。つまり、関数でForEachループを使用していて、ループ内から関数から戻りたい場合は、できません。 ForEachループ内でReturnを発行すると、 Continue []
のように機能するようです。これはただ(それを待つ)ループのために私を投げた。
解決 7
Pillsy およびレオニドシフリン、現在使用しているものは次のとおりです。
SetAttributes[each, HoldAll]; (* each[pattern, list, body] *)
each[pat_, lst_List, bod_] := (* converts pattern to body for *)
(Cases[Unevaluated@lst, pat:>bod]; Null); (* each element of list. *)
each[p_, l_, b_] := (Cases[l, p:>b]; Null); (* (Break/Continue not supported) *)
他のヒント
新しいバージョンのMathematica(6.0+)には、Do []およびTable []の一般化バージョンがあり、イテレータ引数の代替形式を使用することで、ほぼ正確に必要な処理を実行します。たとえば、
Do[
Print[i],
{i, {1, 2, 3}}]
あなたとまったく同じです
ForEach[i_, {1, 2, 3,},
Print[i]]
また、特定のForEach構文が本当に好きな場合は、次のように、それを実装するHoldAll関数を作成できます。
Attributes[ForEach] = {HoldAll};
ForEach[var_Symbol, list_, expr_] :=
ReleaseHold[
Hold[
Scan[
Block[{var = #},
expr] &,
list]]];
ForEach[vars : {__Symbol}, list_, expr_] :=
ReleaseHold[
Hold[
Scan[
Block[vars,
vars = #;
expr] &,
list]]];
これは、パターンではなく変数名としてシンボルを使用しますが、これはDo []やFor []などのさまざまな組み込み制御構造の仕組みです。
HoldAll []関数を使用すると、非常に多様なカスタムコントロール構造をまとめることができます。通常、ReleaseHold [Hold [...]]は、後で評価するMathematicaコードの束を組み立てる最も簡単な方法であり、Block [{x =#}、...]&式本体の変数を任意の値にバインドできます。
以下のドリーヴの質問に答えて、このアプローチを変更して、一意のシンボルのDownValuesを使用して、より任意の破壊を可能にすることができます。
ForEach[patt_, list_, expr_] :=
ReleaseHold[Hold[
Module[{f},
f[patt] := expr;
Scan[f, list]]]]
ただし、現時点では、Casesの上に何かを構築する方がよいと思います。
ForEach[patt_, list_, expr_] :=
With[{bound = list},
ReleaseHold[Hold[
Cases[bound,
patt :> expr];
Null]]]
関数の戻り値を抑制するときにNullを明示的にするのが好きです。 編集:私は以下に示すようにバグを修正しました。私はいつも With
を使用して、評価された式を Hold *
フォームに挿入するのが好きです。
ここでのパーティーに何年も遅れており、これはおそらく「メタ質問」への答えですが、Mathematica(または他の関数型言語)でプログラミングするときに多くの人が最初に苦労していることは構造的観点よりも機能的観点から問題にアプローチする。 Mathematica言語には構造的な構造がありますが、コアで機能的です。
最初の例を検討してください:
ForEach[i_, {1,2,3},
Print[i]
]
複数の人が指摘したように、これは Scan [Print、{1,2,3}]
または Print / @ {1,2,3} として機能的に表現できます。前述のように、可能であれば
Map
よりも Scan
を優先する必要がありますが、 Scan
)。
Mathematicaでは、通常、すべてを行うための12の方法がありますが、これは時には美しく、時にはイライラすることがあります。それを念頭に置いて、2番目の例を検討してください。
ForEach[{i_, j_}, {{1,10}, {2,20}, {3,30}},
Print[i*j]
]
...これは、機能的な観点からより興味深いものです。
可能な機能的解決策の1つは、代わりにリスト置換を使用することです。例:
In[1]:= {{1,10},{2,20},{3,30}}/.{i_,j_}:>i*j
Out[1]= {10,40,90}
...しかし、リストが非常に大きい場合、いわゆる「パターンマッチング」を行っているため、これは不必要に遅くなります。 (たとえば、リストで{a、b}のインスタンスを探し、それらを i
および j
に割り当てます)不要に。
100,000ペアの大きな配列を指定すると、 array = RandomInteger [{1、100}、{10 ^ 6、2}]
、いくつかのタイミングを見ることができます:
ルールの交換は非常に簡単です:
In[3]:= First[Timing[array /. {i_, j_} :> i*j;]]
Out[3]= 1.13844
...しかし、各ペアが実際に List [i、j]
である式構造を利用し、 Times
を次のように適用すると、少し改善できます。各ペアの先頭、各 {i、j}
を Times [i、j]
に変換:
In[4]:= (* f@@@list is the infix operator form of Apply[f, list, 1] *)
First[Timing[Times @@@ array;]]
Out[4]= 0.861267
上記の ForEach [...]
の実装で使用されているように、 Cases
は明らかに最適ではありません:
In[5]:= First[Timing[Cases[array, {i_, j_} :> i*j];]]
Out[5]= 2.40212
... Cases
はルールの置換よりも多くの作業を行うため、一致する要素の出力を1つずつ作成する必要があります。問題を異なる方法で分解することにより、多くのをより良くすることができ、 Times
は Listable
であり、ベクトル化をサポートするという事実を利用できます操作。
Listable
属性は、関数 f
がリスト引数を自動的にスレッド化することを意味します。
In[16]:= SetAttributes[f,Listable]
In[17]:= f[{1,2,3},{4,5,6}]
Out[17]= {f[1,4],f[2,5],f[3,6]}
したがって、 Times
は Listable
であるため、代わりに2つの別々の配列として数値のペアがあった場合:
In[6]:= a1 = RandomInteger[{1, 100}, 10^6];
a2 = RandomInteger[{1, 100}, 10^6];
In[7]:= First[Timing[a1*a2;]]
Out[7]= 0.012661
すごい、かなり高速です!入力が2つの個別の配列として提供されなかった場合(または各ペアに3つ以上の要素がある場合)でも、最適な処理を実行できます。
In[8]:= First[Timing[Times@@Transpose[array];]]
Out[8]= 0.020391
この叙事詩の教訓は、 ForEach
は一般的に、またはMathematicaでも価値のある構造ではないということではありませんが、作業時に同じ結果をより効率的かつエレガントに得ることができることです構造的な考え方ではなく、機能的な考え方で。
組み込みの Scan
は基本的にこれを行いますが、,いですが:
Scan[Print[#]&, {1,2,3}]
要素を分解したい場合は特にugいです:
Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]
次の関数は、 list
の各要素の pattern
を body
に変換することにより、さを回避します。
SetAttributes[ForEach, HoldAll]; ForEach[pat_, lst_, bod_] := Scan[Replace[#, pat:>bod]&, Evaluate@lst]
質問の例のように使用できます。
PS:受け入れられた答えはこれに切り替えるように誘導しました。それは私がそれ以来ずっと使用しているものであり、うまくいくようです(質問に追加した警告を除く):
SetAttributes[ForEach, HoldAll]; (* ForEach[pattern, list, body] *)
ForEach[pat_, lst_, bod_] := ReleaseHold[ (* converts pattern to body for *)
Hold[Cases[Evaluate@lst, pat:>bod];]]; (* each element of list. *)
組み込みのMap関数は、希望どおりに機能します。長い形式で使用できます:
Map [Print、{1,2,3}]
または速記
印刷/ @ {1,2,3}
2番目のケースでは、" Print [Times @@#]& / @ {{1,10}、{2,20}、{3,30}}"
を使用しますMap、MapThread、Apply、およびFunctionのMathematicaヘルプを読むことをお勧めします。彼らは少し慣れることができますが、一度慣れると、もう二度と戻りたくないでしょう!
ここでは、dreevesの最後の回答に基づいて、ブランクなしでパターンを指定できるようにし(TableやDoなどの他の関数と同様の構文を作成)、Casesのlevel引数を使用するわずかな改善があります
SetAttributes[ForEach,HoldAll];
ForEach[patt_/; FreeQ[patt, Pattern],list_,expr_,level_:1] :=
Module[{pattWithBlanks,pattern},
pattWithBlanks = patt/.(x_Symbol/;!MemberQ[{"System`"},Context[x]] :> pattern[x,Blank[]]);
pattWithBlanks = pattWithBlanks/.pattern->Pattern;
Cases[Unevaluated@list, pattWithBlanks :> expr, {level}];
Null
];
テスト:
ForEach[{i, j}, {{1, 10}, {2, 20}, {3, 30}}, Print[i*j]]
ForEach[i, {{1, 10}, {2, 20}, {3, 30}}, Print[i], 2]
Mathematicaにはマップ関数があるため、1つの引数を取る関数 Func
があるとします。次に書くだけ
Func /@ list
Print /@ {1, 2, 3, 4, 5}
戻り値は、リスト内の各要素に適用される関数のリストです。
PrimeQ /@ {10, 2, 123, 555}
{False、True、False、False}