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