Frage

Ich hätte gerne so etwas:

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

Oder allgemeiner, um beliebige Dinge in der Liste, die Sie durchlaufen, zu zerstören, wie zum Beispiel:

each[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

Normalerweise möchten Sie verwenden Map oder andere rein funktionale Konstrukte und vermeiden Sie einen nicht-funktionalen Programmierstil, bei dem Sie Nebenwirkungen verwenden.Aber hier ist ein Beispiel, bei dem ich ein for-each-Konstrukt für äußerst nützlich halte:

Angenommen, ich habe eine Liste von Optionen (Regeln), die Symbole mit Ausdrücken koppeln, z

attrVals = {a -> 7, b -> 8, c -> 9}

Jetzt möchte ich eine Hash-Tabelle erstellen, in der ich die offensichtliche Zuordnung dieser Symbole zu diesen Zahlen vornehme.Ich glaube nicht, dass es einen saubereren Weg gibt, dies zu tun

each[a_ -> v_, attrVals, h[a] = v]

Zusätzliche Testfälle

In diesem Beispiel transformieren wir eine Liste von Variablen:

a = 1;
b = 2;
c = 3;
each[i_, {a,b,c}, i = f[i]]

Nach dem oben Gesagten {a,b,c} sollte bewerten {f[1],f[2],f[3]}.Beachten Sie, dass dies das zweite Argument von bedeutet each sollte unbewertet bleiben, wenn es sich um eine Liste handelt.

Wenn das nicht ausgewertete Formular keine Liste ist, sollte es das zweite Argument auswerten.Zum Beispiel:

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

Das sollte die Werte von drucken b Und c.

Nachtrag:Um es für jeden richtig zu machen, sollte es unterstützen Break[] Und Continue[].Ich bin mir nicht sicher, wie ich das umsetzen soll.Möglicherweise muss es irgendwie in Form von For, While oder Do implementiert werden, da dies die einzigen Schleifenkonstrukte sind, die unterstützt werden Break[] Und Continue[].

Und noch ein Problem mit den bisherigen Antworten:Sie essen Return[]S.Das heißt, wenn Sie eine ForEach-Schleife in einer Funktion verwenden und innerhalb der Schleife von der Funktion zurückkehren möchten, ist dies nicht möglich.Das Ausgeben von Return innerhalb der ForEach-Schleife scheint zu funktionieren Continue[].Das hat mich einfach (warten Sie) aus der Fassung gebracht.

War es hilfreich?

Lösung 7

Dank an Pillsy und Leonid Shifrin, Hier ist, was ich jetzt benutze:

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) *)

Andere Tipps

Neuere Versionen von Mathematica (6.0+) verfügen über verallgemeinerte Versionen von Do[] und Table[], die fast genau das tun, was Sie wollen, indem sie eine alternative Form des Iteratorarguments verwenden.Zum Beispiel,

Do[
  Print[i],
  {i, {1, 2, 3}}]

ist genau wie deins

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

Wenn Ihnen die spezifische ForEach-Syntax wirklich gefällt, können Sie alternativ eine HoldAll-Funktion erstellen, die sie implementiert, etwa so:

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]]];

Hierbei werden Symbole als Variablennamen verwendet, keine Muster, aber so funktionieren die verschiedenen integrierten Kontrollstrukturen wie Do[] und For[].

Mit HoldAll[]-Funktionen können Sie eine Vielzahl benutzerdefinierter Kontrollstrukturen zusammenstellen.ReleaseHold[Hold[...]] ist normalerweise der einfachste Weg, eine Reihe von Mathematica-Code zusammenzustellen, der später ausgewertet werden soll, und Block[{x = #}, ...]& ermöglicht die Bindung von Variablen in Ihrem Ausdruckskörper Welche Werte Sie auch immer wünschen.

Als Antwort auf die Frage von Dreeves unten können Sie diesen Ansatz ändern, um eine willkürlichere Destrukturierung mithilfe der DownValues ​​eines eindeutigen Symbols zu ermöglichen.

ForEach[patt_, list_, expr_] := 
  ReleaseHold[Hold[
     Module[{f}, 
       f[patt] := expr; 
       Scan[f, list]]]]

An diesem Punkt denke ich jedoch, dass es für Sie vielleicht besser wäre, etwas auf Cases aufzubauen.

ForEach[patt_, list_, expr_] :=
  With[{bound = list},
    ReleaseHold[Hold[
       Cases[bound,
         patt :> expr]; 
       Null]]]

Ich mag es, Null explizit zu machen, wenn ich den Rückgabewert einer Funktion unterdrücke. BEARBEITEN:Ich habe den Fehler behoben, auf den Dreeves unten hingewiesen hat;Ich benutze es immer gerne With um ausgewertete Ausdrücke zu interpolieren Hold* Formen.

Ich bin Jahre zu spät zur Party hier, und dies ist vielleicht eher eine Antwort auf die "Meta-Frage" eher eine funktionale als eher strukturelle Sichtweise. Die Mathematica -Sprache hat strukturelle Konstrukte, ist aber im Kern funktional.

Betrachten Sie Ihr erstes Beispiel:

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

Wie mehrere Personen betonten, kann dies funktionell ausgedrückt werden wie Scan[Print, {1,2,3}] oder Print /@ {1,2,3} (Obwohl Sie bevorzugen sollten Scan Über Map Wenn möglich, wie bereits erläutert, kann dies manchmal ärgerlich sein, da es keinen Infix -Operator für gibt Scan).

In Mathematica gibt es normalerweise ein Dutzend Möglichkeiten, alles zu tun, was manchmal schön und manchmal frustrierend ist. Betrachten Sie in diesem Sinne Ihr zweites Beispiel:

ForEach[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

... was aus funktionaler Sicht interessanter ist.

Eine mögliche funktionale Lösung besteht darin, stattdessen den Ersatz für Listen zu verwenden, z. B.:

In[1]:= {{1,10},{2,20},{3,30}}/.{i_,j_}:>i*j
Out[1]= {10,40,90}

... aber wenn die Liste sehr groß wäre, wäre dies unnötig langsam, da wir sogenannte "Muster-Matching" machen (z. i und j) unnötig.

Bei einer großen Auswahl von 100.000 Paaren, array = RandomInteger[{1, 100}, {10^6, 2}], Wir können uns einige Zeiten ansehen:

Die Regelwiedergabe ist ziemlich schnell:

In[3]:= First[Timing[array /. {i_, j_} :> i*j;]]
Out[3]= 1.13844

... aber wir können ein bisschen besser machen, wenn wir die Expressionsstruktur nutzen, in der jedes Paar wirklich ist List[i,j] und bewerben Sie sich Times Als Kopf jedes Paares drehen Sie sich jeweils {i,j} hinein 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

Wie bei der Implementierung von verwendet ForEach[...] Oben, Cases ist ausgesprochen suboptimal:

In[5]:= First[Timing[Cases[array, {i_, j_} :> i*j];]]
Out[5]= 2.40212

... seit Cases Arbeitet mehr als nur der Regelersatz, der eine Ausgabe von passenden Elementen einzeln erstellen muss. Es stellt sich heraus, dass wir a machen können viel besser, indem das Problem anders zerlegt wird und die Tatsache nutze, dass Times ist Listable, und unterstützt den vektorisierten Betrieb.

Das Listable Attribut bedeutet, dass eine Funktion f wird automatisch alle Listenargumente übergeben:

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

Also seit Times ist Listable, Wenn wir stattdessen die Zahlenpaare als zwei separate Arrays hatten:

In[6]:= a1 = RandomInteger[{1, 100}, 10^6];
        a2 = RandomInteger[{1, 100}, 10^6];

In[7]:= First[Timing[a1*a2;]]
Out[7]= 0.012661

Wow, ziemlich schneller! Auch wenn der Eingang nicht als zwei separate Arrays bereitgestellt wurde (oder Sie haben mehr als zwei Elemente in jedem Paar), können wir immer noch etwas Optimales tun:

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

Die Moral dieses Epos ist das nicht ForEach Ist nicht ein wertvolles Konstrukt im Allgemeinen oder sogar in Mathematica, aber Sie können oft die gleichen Ergebnisse effizienter und eleganter erzielen, wenn Sie in einer funktionalen Denkweise arbeiten, anstatt in strukturellem.

Das eingebaute Scan Grundsätzlich tut es dies, obwohl es hässlicher ist:

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

Es ist besonders hässlich, wenn Sie die Elemente zerstören möchten:

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

Die folgende Funktion vermeidet die Hässlichkeit durch Konvertierung pattern zu body für jedes Element von list.

SetAttributes[ForEach, HoldAll];
ForEach[pat_, lst_, bod_] :=  Scan[Replace[#, pat:>bod]&, Evaluate@lst]

Dies kann wie im Beispiel in der Frage verwendet werden.

PS: Die akzeptierte Antwort hat mich dazu veranlasst, darauf zu wechseln, was ich seitdem benutze, und es scheint großartig zu funktionieren (mit Ausnahme der Einschränkung, die ich an die Frage angehängt habe):

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.        *)

Die integrierte Kartenfunktion macht genau das, was Sie wollen. Es kann in langer Form verwendet werden:

Karte [print, {1,2,3}

oder kurz

Print /@ {1,2,3}

In Ihrem zweiten Fall würden Sie "Print [Times @@#] &/@{{1,10}, {2,20}, {3,30}}" verwenden ".

Ich würde empfehlen, die Mathematica -Hilfe auf Karte, Mapthread, Anwendung und Funktion zu lesen. Sie können sich ein bisschen gewöhnen, aber wenn Sie es sind, möchten Sie nie zurückkehren!

Hier ist eine geringfügige Verbesserung, die auf der letzten Antwort von Dreeves basiert, die es ermöglicht, das Muster ohne Leerzeichen anzugeben (die Syntax ähnlich wie bei Tabelle oder DO) und das Level -Argument von Fällen verwendet

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
   ];

Tests:

ForEach[{i, j}, {{1, 10}, {2, 20}, {3, 30}}, Print[i*j]]
ForEach[i, {{1, 10}, {2, 20}, {3, 30}}, Print[i], 2]

Mathematica hat Kartenfunktionen, also sagen wir, Sie haben eine Funktion Funcein Argument nehmen. Dann schreibe einfach

Func /@ list

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

Der Rückgabewert ist eine Liste der Funktion, die auf jedes Element in der In-Liste angewendet wird.

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

wird zurückkehren {False,True,False,False}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top