Domanda

Vorrei qualcosa del genere:

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

O, più in generale, per destrutturare cose arbitrarie nell'elenco che stai ripetendo, come:

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

Solitamente si desidera utilizzare Map o altri costrutti puramente funzionali e evitare uno stile di programmazione non funzionale in cui si utilizzano effetti collaterali. Ma ecco un esempio in cui penso che un costrutto for-each sia estremamente utile:

Supponi di avere un elenco di opzioni (regole) che accoppiano i simboli alle espressioni, come

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

Ora voglio creare una tabella hash in cui eseguo l'ovvia mappatura di quei simboli su quei numeri. Non penso che ci sia un modo più pulito di farlo rispetto a

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

Casi di prova aggiuntivi

In questo esempio, trasformiamo un elenco di variabili:

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

Dopo quanto sopra, {a, b, c} dovrebbe valutare {f [1], f [2], f [3]} . Nota che ciò significa che il secondo argomento per ogni dovrebbe essere considerato non valutato se si tratta di un elenco.

Se il modulo non valutato non è un elenco, dovrebbe valutare il secondo argomento. Ad esempio:

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

Questo dovrebbe stampare i valori di b e c .

Addendum : per eseguire correttamente ciascuna operazione, dovrebbe supportare Break [] e Continue [] . Non sono sicuro di come implementarlo. Forse dovrà essere in qualche modo implementato in termini di For, While o Do poiché questi sono gli unici costrutti di loop che supportano Break [] e Continue [] .

E un altro problema con le risposte finora: mangiano Return [] s. Cioè, se si utilizza un ciclo ForEach in una funzione e si desidera tornare dalla funzione dall'interno del ciclo, non è possibile. Emissione Return all'interno del ciclo ForEach sembra funzionare come Continue [] . Questo solo (aspetta) mi ha gettato un ciclo.

È stato utile?

Soluzione 7

Grazie a Pillsy e Leonid Shifrin , ecco cosa sto usando ora:

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

Altri suggerimenti

Le versioni più recenti di Mathematica (6.0+) hanno versioni generalizzate di Do [] e Table [] che fanno quasi esattamente quello che vuoi, prendendo una forma alternativa di argomento iteratore. Ad esempio,

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

è esattamente come il tuo

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

In alternativa, se ti piace davvero la sintassi specifica di ForEach, puoi creare una funzione HoldAll che la implementa, in questo modo:

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

Questo usa simboli come nomi di variabili, non schemi, ma è così che funzionano le varie strutture di controllo integrate come Do [] e For [].

Le funzioni

??HoldAll [] consentono di mettere insieme una varietà piuttosto ampia di strutture di controllo personalizzate. ReleaseHold [Hold [...]] è in genere il modo più semplice per assemblare un gruppo di codice Mathematica da valutare in seguito, e Block [{x = #}, ...] & amp; permette alle variabili nel tuo corpo di espressione di essere legate a qualunque valore tu voglia.

In risposta alla seguente domanda di dreeves, puoi modificare questo approccio per consentire una più arbitraria destrutturazione usando i DownValues ??di un simbolo univoco.

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

A questo punto, tuttavia, penso che potresti stare meglio costruendo qualcosa sopra Casi.

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

Mi piace rendere esplicito Null quando sopprimo il valore restituito di una funzione. MODIFICA : ho corretto l'errore segnalato sotto; Mi piace sempre usare With per interpolare le espressioni valutate nei moduli Hold * .

Sono in ritardo di anni alla festa qui, e questa è forse più una risposta alla "meta-domanda", ma qualcosa con cui molte persone inizialmente hanno difficoltà a programmare in Mathematica (o in altri linguaggi funzionali) è affrontare un problema da un punto di vista funzionale piuttosto che strutturale. Il linguaggio Mathematica ha costrutti strutturali, ma è funzionale al suo interno.

Considera il tuo primo esempio:

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

Come sottolineato da più persone, questo può essere espresso funzionalmente come Scan [Print, {1,2,3}] o Print / @ {1,2,3} (anche se dovresti preferire Scan su Map quando possibile, come spiegato in precedenza, ma a volte può essere fastidioso poiché non esiste un operatore infix per Scan ).

In Mathematica, di solito ci sono una dozzina di modi per fare tutto, che a volte è bello e a volte frustrante. Con questo in mente, considera il tuo secondo esempio:

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

... che è più interessante da un punto di vista funzionale.

Una possibile soluzione funzionale è invece utilizzare la sostituzione dell'elenco, ad esempio:

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

... ma se l'elenco fosse molto grande, ciò sarebbe inutilmente lento poiché stiamo facendo il cosiddetto "pattern matching" " (ad esempio, cercando istanze di {a, b} nell'elenco e assegnandole inutilmente a i e j ).

Data una vasta gamma di 100.000 coppie, array = RandomInteger [{1, 100}, {10 ^ 6, 2}] , possiamo esaminare alcune tempistiche:

La sostituzione delle regole è piuttosto rapida:

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

... ma possiamo fare un po 'meglio se sfruttiamo la struttura dell'espressione in cui ogni coppia è veramente Elenco [i, j] e applichiamo Times come la testa di ciascuna coppia, trasformando ogni {i, j} in 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

Come utilizzato nell'implementazione di ForEach [...] sopra, Casi è decisamente non ottimale:

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

... poiché Casi funziona di più rispetto alla semplice sostituzione della regola, dovendo creare un output di elementi corrispondenti uno per uno. Si scopre che possiamo fare un lotto decomposendo il problema in modo diverso e trarre vantaggio dal fatto che Times è Listable e supporta vettorializzare operazione.

L'attributo Listable indica che una funzione f eseguirà automaticamente il thread su qualsiasi argomento dell'elenco:

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

Quindi, poiché Times è Listable , se invece avessimo avuto le coppie di numeri come due array separati:

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 , un po 'più veloce! Anche se l'input non è stato fornito come due array separati (o se hai più di due elementi in ciascuna coppia), possiamo comunque fare qualcosa di ottimale:

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

La morale di questa epopea non è che ForEach non sia un costrutto prezioso in generale, o nemmeno in Mathematica, ma che spesso puoi ottenere gli stessi risultati in modo più efficiente ed elegante quando lavori in una mentalità funzionale, piuttosto che strutturale.

In pratica il Scan integrato fa questo, sebbene sia più brutto:

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

È particolarmente brutto quando vuoi distruggere gli elementi:

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

La seguente funzione evita la bruttezza convertendo pattern in body per ciascun elemento dell'elenco .

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

che può essere utilizzato come nell'esempio nella domanda.

PS: la risposta accettata mi ha indotto a passare a questo, che è quello che uso da allora e sembra funzionare alla grande (tranne per l'avvertenza che ho aggiunto alla domanda):

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

La funzione Mappa integrata fa esattamente quello che vuoi. Può essere usato in forma lunga:

Mappa [Stampa, {1,2,3}]

o abbreviato

Stampa / @ {1,2,3}

Nel tuo secondo caso, useresti " Print [Times @@ #] & amp; / @ {{1,10}, {2,20}, {3,30}} "

Consiglio di leggere la guida di Mathematica su Map, MapThread, Apply e Function. Possono prendere un po 'di tempo per abituarsi, ma una volta che ci sei, non vorrai più tornare indietro!

Ecco un leggero miglioramento basato sull'ultima risposta di dreeves che consente di specificare il pattern senza Blank (rendendo la sintassi simile ad altre funzioni come Table o Do) e che utilizza l'argomento level di Cases

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

Test:

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

Mathematica ha funzioni cartografiche, quindi supponiamo che tu abbia una funzione Func che accetta un argomento. Quindi scrivi

Func /@ list

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

Il valore restituito è un elenco della funzione applicata a ciascun elemento dell'elenco.

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

restituirà {False,True,False,False}

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top