while for ciclos loops language-features wolfram-mathematica

loops - for - ciclos en python



Para cada bucle en Mathematica (7)

La función de mapa integrada hace exactamente lo que desea. Se puede usar en forma larga:

Mapa [Imprimir, {1,2,3}]

o mano corta

Imprimir / @ {1,2,3}

En su segundo caso, usaría "Imprimir [Times @@ #] & / @ {{1,10}, {2,20}, {3,30}}"

Recomiendo leer la ayuda de Mathematica en Map, MapThread, Apply y Function. Pueden tardar un poco en acostumbrarse, pero una vez que lo esté, ¡nunca más querrá regresar!

Me gustaría algo como esto:

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

O, de manera más general, para desestructurar cosas arbitrarias en la lista por la que está pasando, como:

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

Por lo general, desea utilizar Map u otras construcciones puramente funcionales y evitar un estilo de programación no funcional donde utiliza efectos secundarios. Pero aquí hay un ejemplo en el que creo que una construcción para cada uno es sumamente útil:

Digamos que tengo una lista de opciones (reglas) que emparejan símbolos con expresiones, como

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

Ahora quiero hacer una tabla hash donde hago el mapeo obvio de esos símbolos a esos números. No creo que haya una forma más limpia de hacerlo que

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

Casos de prueba adicionales

En este ejemplo, transformamos una lista de variables:

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

Después de lo anterior, {a, b, c} debería evaluar a {f [1], f [2], f [3]}. Tenga en cuenta que eso significa que el segundo argumento para "cada" debe mantenerse sin evaluar si se trata de una lista.

Si la forma no evaluada no es una lista, debe evaluar el segundo argumento. Por ejemplo:

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

Eso debería imprimir los valores de b y c.

Adición : para hacer adecuadamente para cada uno, debería soportar Break [] y Continue []. No estoy seguro de cómo implementar eso. Quizás de alguna manera deba implementarse en términos de For, While o Do, ya que son las únicas construcciones de bucle que admiten Break [] y Continue [].

Y otro problema con las respuestas hasta ahora: comen Return [] s. Es decir, si está utilizando un bucle ForEach en una función y desea regresar de la función desde el bucle, no puede. La emisión de devolución dentro del bucle ForEach parece funcionar como Continuar []. Esto solo (esperar) me lanzó a un bucle.


Mathematica tiene funciones de mapa, así que digamos que tiene una función Func tomando un argumento. Entonces solo escribe

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

El valor de retorno es una lista de la función aplicada a cada elemento en la lista interna.

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

devolverá {False,True,False,False}


Las versiones más nuevas de Mathematica (6.0+) tienen versiones generalizadas de Do [] y Table [] que hacen casi exactamente lo que usted desea, tomando una forma alternativa de argumento iterador. Por ejemplo,

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

es exactamente como tu

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

De forma alternativa, si realmente te gusta la sintaxis ForEach específica, puedes hacer una función HoldAll que la implemente, así:

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

Esto utiliza símbolos como nombres de variables, no patrones, pero así es como funcionan las diversas estructuras de control integradas como Do [] y For [].

Las funciones HoldAll [] le permiten armar una amplia variedad de estructuras de control personalizadas. ReleaseHold [Retener [...]] suele ser la forma más fácil de ensamblar un montón de código de Mathematica para evaluarlo más tarde, y Bloquear [{x = #}, ...] & permite que las variables de tu cuerpo de expresión estén ligadas a cualesquiera valores que quieras

En respuesta a la pregunta de Dreeves a continuación, puede modificar este enfoque para permitir una desestructuración más arbitraria utilizando los valores descendentes de un símbolo único.

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

En este punto, sin embargo, creo que será mejor que construyas algo encima de Cases.

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

Me gusta hacer que Null sea explícito cuando suprimo el valor de retorno de una función. EDITAR : Solucioné el error señalado como dreeves a continuación; Siempre me gusta utilizar With para interpolar expresiones evaluadas en formularios Hold* .


Llegué tarde a la fiesta aquí, y esta es quizás más una respuesta a la "meta-pregunta", pero algo que mucha gente inicialmente tiene dificultades cuando la programación en Mathematica (u otros lenguajes funcionales) se está acercando a un problema de un punto de vista funcional en lugar de estructural. El lenguaje de Mathematica tiene construcciones estructurales, pero es funcional en su núcleo.

Considera tu primer ejemplo:

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

Como señalaron varias personas, esto se puede expresar funcionalmente como Scan[Print, {1,2,3}] o Print /@ {1,2,3} (aunque debería favorecer Scan sobre el Map cuando sea posible, como se explicó anteriormente, pero a veces puede ser molesto ya que no hay un operador infijo para Scan ).

En Mathematica, generalmente hay una docena de maneras de hacer todo, lo cual a veces es hermoso y algunas veces frustrante. Con eso en mente, considere su segundo ejemplo:

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

... que es más interesante desde un punto de vista funcional.

Una posible solución funcional es utilizar reemplazo de lista, por ejemplo:

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

... pero si la lista fuera muy grande, sería innecesariamente lenta ya que estamos haciendo lo que se conoce como "coincidencia de patrones" (por ejemplo, buscando instancias de {a, b} en la lista y asignándolas a i y j ) innecesariamente

Dado un gran conjunto de 100.000 pares, array = RandomInteger[{1, 100}, {10^6, 2}] , podemos ver algunos tiempos:

El reemplazo de reglas es bastante rápido:

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

... pero podemos hacerlo un poco mejor si aprovechamos la estructura de expresión donde cada par es realmente List[i,j] y aplicamos Times como la cabeza de cada par, convirtiendo cada {i,j} en 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

Tal como se usa en la implementación de ForEach[...] anterior, Cases es decididamente subóptimo:

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

... desde Cases hace más trabajo que solo el reemplazo de reglas, teniendo que crear una salida de elementos coincidentes uno a uno. Resulta que podemos hacer mucho mejor al descomponer el problema de manera diferente, y aprovechar el hecho de que Times es Listable , y es compatible con la operación vectorizada.

El atributo Listable significa que una función f se Listable automáticamente sobre cualquier argumento de lista:

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

Entonces, dado que Times es Listable , si tuviéramos los pares de números como dos matrices separadas:

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 , ¡bastante más rápido! Incluso si la entrada no se proporcionó como dos matrices separadas (o si tiene más de dos elementos en cada par), aún podemos hacer algo óptimo:

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

La moraleja de esta epopeya no es que ForEach no sea una construcción valiosa en general, ni siquiera en Mathematica, sino que a menudo puede obtener los mismos resultados de manera más eficiente y más elegante cuando se trabaja con una mentalidad funcional, en lugar de estructural. .


El Scan integrado básicamente hace esto, aunque es más feo:

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

Es especialmente feo cuando quieres desestructurar los elementos:

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

La siguiente función evita la fealdad al convertir el pattern en body para cada elemento de la list .

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

que se puede usar como en el ejemplo en la pregunta.

PD: La respuesta aceptada me indujo a cambiar a esto, que es lo que he estado usando desde entonces y parece funcionar muy bien (a excepción de la advertencia que agregué a la pregunta):

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


Aquí hay una ligera mejora basada en la última respuesta de Dreeves que permite especificar el patrón sin Blank (haciendo la sintaxis similar a otras funciones como Table o Do) y que usa el argumento de nivel 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 ];

Pruebas:

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


Gracias a Pillsy y Leonid Shifrin , esto es lo que estoy usando:

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