Question

Je voudrais quelque chose comme ça:

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

Ou, plus généralement, pour déstructurer des éléments arbitraires dans la liste sur laquelle vous faites une boucle, par exemple:

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

Généralement, vous souhaitez utiliser Map ou d'autres constructions purement fonctionnelles et éviter un style de programmation non fonctionnel dans lequel vous utilisez des effets secondaires. Mais voici un exemple où je pense qu'une construction pour chaque est extrêmement utile:

Disons que j'ai une liste d'options (règles) qui associent des symboles à des expressions, comme

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

Maintenant, je veux créer une table de hachage dans laquelle je mappe de manière évidente ces symboles avec ces nombres. Je ne pense pas qu'il y ait un moyen plus propre de le faire que

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

Cas de test supplémentaires

Dans cet exemple, nous transformons une liste de variables:

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

Après ce qui précède, {a, b, c} doit être évalué à {f [1], f [2], f [3]} . Notez que cela signifie que le deuxième argument de chaque doit être conservé non évalué s'il s'agit d'une liste.

Si le formulaire non évalué n'est pas une liste, il convient d'évaluer le deuxième argument. Par exemple:

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

Cela devrait afficher les valeurs de b et c .

Addendum : pour le faire correctement, il doit prendre en charge Break [] et Continuer [] . Je ne suis pas sûr de savoir comment mettre cela en œuvre. Peut-être devra-t-il être implémenté en termes de For, While ou Do puisque ce sont les seules constructions de boucle prenant en charge Break [] et Continue [] .

Et un autre problème avec les réponses à ce jour: ils mangent des retours [] . Autrement dit, si vous utilisez une boucle ForEach dans une fonction et souhaitez revenir de la fonction depuis la boucle, vous ne pouvez pas. L'émission Return à l'intérieur de la boucle ForEach semble fonctionner comme Continuer [] . Cela vient de (attendre) m'a lancé une boucle.

Était-ce utile?

La solution 7

Merci à Pillsy et Leonid Shifrin , voici ce que j'utilise maintenant:

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

Autres conseils

Les versions les plus récentes de Mathematica (6.0+) ont des versions généralisées de Do [] et de Table [] qui font presque exactement ce que vous voulez, en prenant une autre forme d'argument d'itérateur. Par exemple,

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

est exactement comme votre

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

Alternativement, si vous aimez vraiment la syntaxe spécifique de ForEach, vous pouvez créer une fonction HoldAll qui l'implémente, comme suit:

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

Ceci utilise des symboles en tant que noms de variables, pas de modèles, mais c'est ainsi que fonctionnent les différentes structures de contrôle intégrées comme Do [] et For [].

Les fonctions

HoldAll [] vous permettent de créer une grande variété de structures de contrôle personnalisées. ReleaseHold [Hold [...]] est généralement le moyen le plus simple d'assembler une série de code Mathematica à évaluer ultérieurement. Block [{x = #}, ...] & amp; permet aux variables de votre corps d'expression d'être liées à toutes les valeurs souhaitées.

En réponse à la question ci-dessous relative à dreeves, vous pouvez modifier cette approche afin de permettre une déstructuration plus arbitraire à l'aide des valeurs réduites d'un symbole unique.

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

À ce stade, cependant, je pense que vous feriez mieux de construire quelque chose en plus de Cases.

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

J'aime rendre Null explicite lorsque je supprime la valeur de retour d'une fonction. MODIFIER : j'ai corrigé le bogue signalé ci-dessous; J'aime toujours utiliser With pour interpoler des expressions évaluées dans des formes Hold * .

Je suis des années en retard à la fête ici, et c’est peut-être plus une réponse à la "méta-question", mais quelque chose que beaucoup de gens ont d’abord eu du mal à faire quand programmer avec Mathematica (ou d’autres langages fonctionnels) est aborder un problème d'un point de vue fonctionnel plutôt que structurel. Le langage Mathematica a des constructions structurelles, mais il est essentiellement fonctionnel.

Considérez votre premier exemple:

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

Comme plusieurs personnes l’ont souligné, cela peut s’exprimer fonctionnellement par Numériser [Imprimer, {1,2,3}] ou Imprimer / @ {1,2,3} (bien que vous devriez préférer Analyser à Mapper lorsque cela est possible, comme expliqué précédemment, mais cela peut être gênant par moment puisqu'il n'y a pas d'opérateur d'infixe pour Scan ).

Dans Mathematica, il existe généralement une douzaine de façons de tout faire, ce qui est parfois beau et parfois frustrant. Gardez cela à l’esprit, considérez votre deuxième exemple:

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

... ce qui est plus intéressant d'un point de vue fonctionnel.

Une solution fonctionnelle possible consiste à utiliser plutôt le remplacement de liste, par exemple:

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

... mais si la liste était très longue, cela serait inutilement lent, car nous faisons ce que nous appelons des "correspondances de modèle". (par exemple, rechercher des instances de {a, b} dans la liste et les affecter à i et à j ) inutilement.

Etant donné un grand tableau de 100 000 paires, array = RandomInteger [{1,100}, {10 ^ 6, 2}] , nous pouvons examiner certains timings:

Le remplacement de règles est assez rapide:

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

... mais nous pouvons faire un peu mieux si nous tirons parti de la structure d'expression où chaque paire est vraiment Liste [i, j] et appliquons Times la tête de chaque paire, en transformant chaque {i, j} en fois [i, j] :

In[4]:= (* f@@@list is the infix operator form of Apply[f, list, 1] *)
    First[Timing[Times @@@ array;]]
Out[4]= 0.861267

Tel qu'utilisé dans la mise en œuvre de ForEach [...] ci-dessus, Cases est décidément sous-optimal:

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

... depuis Cases ne se limite pas au remplacement de règles, il doit générer une sortie des éléments correspondants un par un. Il s'avère que nous pouvons faire mieux beaucoup en décomposant le problème différemment et en tirant parti du fait que Times est Listable et prend en charge la vectorisation. opération.

L'attribut Listable signifie qu'une fonction f sera automatiquement intégrée aux arguments de la liste:

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

Donc, puisque Times est Listable , si nous avions plutôt les paires de nombres sous la forme de deux tableaux distincts:

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 peu plus vite! Même si l'entrée n'a pas été fournie sous forme de deux tableaux distincts (ou si vous avez plus de deux éléments dans chaque paire), vous pouvez toujours faire quelque chose d'optimal:

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

La morale de cette épopée n’est pas que ForEach ne soit pas une construction valable en général, ni même dans Mathematica, mais que vous puissiez souvent obtenir les mêmes résultats de manière plus efficace et plus élégante lorsque vous travaillez. dans un état d'esprit fonctionnel, plutôt que structurel.

Le Scan intégré fait essentiellement ceci, bien que ce soit plus laid:

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

C'est particulièrement moche quand on veut déstructurer les éléments:

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

La fonction suivante évite la laideur en convertissant le motif en corps pour chaque élément de la liste .

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

qui peut être utilisé comme dans l'exemple de la question.

PS: La réponse acceptée m'a incité à passer à ceci. C'est ce que j'utilise depuis et il semble bien fonctionner (à l'exception de la mise en garde que j'ai jointe à la question):

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 fonction Map intégrée fait exactement ce que vous voulez. Il peut être utilisé sous forme longue:

Carte [Imprimer, {1,2,3}]

ou short-hand

Imprimer / @ {1,2,3}

Dans votre deuxième cas, vous utiliseriez " Imprimer [Times @@ #] & @ {{1,10}, {2,20}, {3,30}} "

Je vous recommande de lire l'aide de Mathematica sur Map, MapThread, Apply et Function. Ils peuvent prendre un peu de temps pour s'y habituer, mais une fois que vous l'êtes, vous ne voudrez plus jamais revenir!

Voici une légère amélioration basée sur la dernière réponse de dreeves qui permet de spécifier le modèle sans Blank (rendant la syntaxe similaire à d’autres fonctions comme Table ou Do) et qui utilise l’argument de niveau de 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
   ];

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 ayant des fonctions de carte, disons que vous avez une fonction Func prenant un argument. Ensuite, écrivez simplement

Func /@ list

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

La valeur de retour est une liste de la fonction appliquée à chaque élément de la liste en entrée.

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

renverra {False, True, False, False}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top