22

私はこのようなものが欲しいです:

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]}です。これは、リストの場合、 の 2 番目の引数が評価されないままにしてeachおく必要があることを意味することに注意してください。

未評価のフォームがリストでない場合、2 番目の引数を評価する必要があります。例えば:

each[i_, Rest[{a,b,c}], Print[i]]

bとの値が表示されcます。

補遺: for-each を適切に行うには、 and をサポートする必要がBreak[]ありContinue[]ます。それを実装する方法がわかりません。おそらく、For、While、または Do に関して何らかの方法で実装する必要があります。これは、 Break[]andをサポートする唯一のループ構造であるためContinue[]です。

そして、これまでの回答のも​​う 1 つの問題: 彼らは s を食べReturn[]ます。つまり、関数で ForEach ループを使用していて、ループ内から関数から戻りたい場合はできません。ForEach ループ内で Return を発行すると、 のように機能するようContinue[]です。これはちょうど(それを待って)私をループに投げ込みました。

4

7 に答える 7

11

私はここでのパーティーに何年も遅れており、これはおそらく「メタ質問」への回答ですが、Mathematica (または他の関数型言語) でのプログラミングが問題に近づいているときに、多くの人が最初は苦労するものです。構造的ではなく機能的な視点。Mathematica 言語には構造的な構造がありますが,その核心は機能的です.

最初の例を考えてみましょう:

ForEach[i_, {1,2,3},
  Print[i]
]

何人かが指摘したように、これは関数的にScan[Print, {1,2,3}]orとして表現できます (ただし、前に説明したように、可能なPrint /@ {1,2,3}場合は or を優先する必要がありますScanが、中置演算子がないため、時々煩わしい場合があります)。MapScan

Mathematica では通常、あらゆることを行う方法が十数通りありますが、これは美しい場合もあれば、イライラする場合もあります。それを念頭に置いて、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} のインスタンスを検索し、それらをiandに割り当てる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つずつ作成する必要があるためです。問題を別の方法で分解し、 であるという事実を利用して、ベクトル化された操作をサポートすることで、はるかにうまくいくことわかりました。TimesListable

この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]}

したがって、TimesisListableであるため、代わりに数値のペアを 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 つの別個の配列として提供されなかった場合 (または各ペアに 2 つ以上の要素がある場合) でも、最適な処理を行うことができます。

In[8]:= First[Timing[Times@@Transpose[array];]]
Out[8]= 0.020391

この叙事詩の教訓は、それForEachが一般的に、あるいは Mathematica でさえ価値のある構造ではないということではありませんが、構造的な考え方ではなく、機能的な考え方で作業すると、同じ結果をより効率的かつエレガントに取得できることが多いということです。

于 2010-03-05T23:40:30.633 に答える
9

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 = #}, ...]& を使用すると、式本体の変数をバインドできますどんな値でも構いません。

以下の dreeves の質問に応えて、このアプローチを変更して、一意のシンボルの 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*

于 2009-08-11T14:39:20.677 に答える
8

ビルトインはScan基本的にこれを行いますが、それは醜いです:

    Scan[Print[#]&, {1,2,3}]

要素を分解したい場合は特に醜いです:

    Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]

次の関数は、の各要素patternをforに変換することで醜さを回避します。bodylist

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.        *)
于 2008-10-01T23:28:17.603 に答える
3

組み込みの Map 関数は、まさにあなたが望むことを行います。長い形式で使用できます。

Map[印刷, {1,2,3}]

または省略形

印刷 /@ {1,2,3}

2 番目のケースでは、「Print[Times@@#]&/@{{1,10}, {2,20}, {3,30}}」を使用します。

Map、MapThread、Apply、および Function に関する Mathematica のヘルプを読むことをお勧めします。慣れるまで少し時間がかかるかもしれませんが、一度慣れると二度と戻りたくないでしょう!

于 2008-11-23T00:00:46.497 に答える
2

ここでは、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]
于 2011-08-31T15:57:11.213 に答える
1

Mathematica にはマップ関数があるため、Func引数を 1 つ取る関数があるとします。あとは書くだけ

Func /@ list

Print /@ {1, 2, 3, 4, 5}

戻り値は、リスト内の各要素に適用される関数のリストです。

PrimeQ /@ {10, 2, 123, 555}

戻ります{False,True,False,False}

于 2009-08-24T16:48:44.363 に答える
1

PillsyLeonid Shifrinのおかげで、私が今使っているものは次のとおりです。

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) *)
于 2011-01-15T16:30:38.153 に答える