2011-04-21 18 views
10

La función MapAll fue visto como suficientemente importante como para justificar la forma corta //@, sin embargo, rara vez se utilizan, sobre todo en comparación con otros como /@, /. y @@@ que uso en casi todas partes.usos para MapAll (// @)

  • ¿Qué aplicaciones mejor apalancamiento MapAll?

  • ¿Se usa principalmente en ciertos campos o estilos de programación?

  • ¿Con qué frecuencia se puede utilizar en comparación con otros operadores?

+3

http: // libros .google.com/libros? id = 3OtUpdFiXvkC y pg = PA525 y lpg = PA525 y dq = Mathematica + mapall y fuente = BL & ots = hQBU27AfXZ y sig = LI3VDlvNq4AkVIDA_IgzUJFBPaQ & hl = es & ei = vHawTYroJY-w0QHauKiiCQ & SA = X & oi = book_result y ct = resultado # v = OnePage y q = Mathematica% 20mapall & f = false –

+0

tengo una Es difícil tratar de recordar si lo he usado en absoluto ... –

+0

Gracias a todos por sus respuestas. Me costó mucho elegir cuál aceptar. –

Respuesta

13

//@ es un "traspaso de árbol posterior a la orden". Visita cada nodo en una estructura de árbol, los hijos de cada nodo son visitados antes que el nodo en sí. La función suministrada se llama con cada nodo como su argumento, los hijos del nodo ya han sido "expandidos" por una llamada previa. Las estructuras de datos de árbol son comunes, junto con la necesidad de atravesarlas. Pero me atrevo a decir que el caso de uso primario para //@ en un contexto de Mathematica es implementar evaluadores.inicio

Vamos mediante la creación de una expresión con estructura de árbol al azar:

In[1]:= 
     $expr = 500 //. 
      n_Integer /; RandomInteger[100] < n :> 
      RandomChoice[{p, m}] @@ RandomInteger[Floor[n/2], 2] 
     $expr//TreeForm 

Out[2]= p[m[p[34, 22], m[11, 24]], p[m[6, 7], 10]] 

expression tree

Digamos que queremos crear un evaluador para un mini-idioma con expresiones de esta forma, donde p medios "más" y m significa menos. Podemos escribir un evaluador de descenso recursivo para este mini-lengua así:

In[4]:= 
     eval1[p[a_, b_]] := eval1[a] + eval1[b] 
     eval1[m[a_, b_]] := eval1[a] - eval1[b] 
     eval1[a_] := a 

In[7]:= 
     eval1[$expr] 

Out[7]= 78 

Se vuelve tedioso tener que escribir de forma explícita las llamadas recursivas a eval1 en cada una de las reglas. Además, es fácil olvidarse de agregar la llamada recursiva en una regla. Consideremos ahora la siguiente versión del mismo evaluador:

In[8]:= 
     eval2[p[a_, b_]] := a + b 
     eval2[m[a_, b_]] := a - b 
     eval2[a_] := a 

El "ruido" de las llamadas recursivas se ha eliminado de manera que las reglas son más fáciles de leer. ¿Seguramente podemos encontrar alguna manera de insertar automáticamente las llamadas recursivas necesarias? Introduzca //@:

In[11]:= 
     eval2 //@ $expr 
Out[11]= 78 

No sólo lo que necesita. Con un ligero abuso de la terminología tomada de la programación funcional, podemos decir que hemos elevado eval2 para que sea una función de descenso recursivo. Puedes ver el efecto en el siguiente diagrama.

In[12]:= 
     "eval2" //@ $expr // TreeForm 

eval2 expansion

PostScript

En Mathematica siempre hay muchas maneras de lograr un efecto. Por esta evaluador juguete, toda la discusión anterior es una exageración:

In[13]:= 
     $expr /. {p -> Plus, m -> Subtract} 

Out[13]= 78 

... aunque sólo fuera siempre tan fácil de comprobar si un evaluador estaba dando los resultados correctos :)

+0

+1 Ejemplo bien escrito. –

+0

+1 ¡Buen ejemplo! Una alternativa a escribir un 'eval' explícito sería inclinar al evaluador de mma en la dirección correcta. Una de las técnicas que uso para fines similares es la "congelación de código": aproximadamente, oculto dinámicamente ciertas cabezas para que permanezcan inertes durante la generación del código. El resultado es que el código mma se genera (o, más bien, se expande) mediante una evaluación parcial del código inicial. Pero esto es más relevante para la construcción de compiladores para mma, en lugar de evaluadores/intérpretes. –

4

lo usaría como una manera perezosa para aplicar expresiones algebraicas en objetos funciones algebraicas no trabajar con:

In[13]:= ser = 1/(1 + x)^a + O[x]^4 

Out[13]= SeriesData[x, 0, { 
1, -a, Rational[1, 2] (a + a^2), 
    Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1] 

In[14]:= Factor[ser] 

Out[14]= SeriesData[x, 0, { 
1, -a, Rational[1, 2] (a + a^2), 
    Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1] 

In[15]:= MapAll[Factor, ser] 

Out[15]= SeriesData[x, 0, { 
1, -a, Rational[1, 2] a (1 + a), 
    Rational[-1, 6] a (1 + a) (2 + a)}, 0, 4, 1] 
+3

Hoy aprendí que al agregar O [x]^4 a 1/(1 + x)^a se fuerza una expansión en serie en este último. Y, sin embargo, está ahí en los documentos, bien escondido bajo el encabezado 'Ámbito'. –

7

Yo no lo uso, pero tiene un comportamiento divertido para funciones catalogables . Por ejemplo:

Si desea que cada elemento de una lista tenga una función aplicada un número de veces dependiendo de su profundidad anidada en la lista, supongo que esta es la única vez que la he visto usar.

SetAttributes[f, Listable] 

(f //@ {{a}, {{b}}, c}) // Flatten 
 {f[f[f[a]]], f[f[f[f[b]]]], f[f[c]]}

En general, aunque me imagino que podría utilizar ReplaceAll cada vez que usaría esto.

9

Lo usé varias veces para hacer una representación inerte del código que puede ejecutarse y que desea transformar o desestructurar sin ninguna evaluación. He aquí un ejemplo:

ClearAll[myHold, makeInertCode]; 
SetAttributes[{myHold, makeInertCode}, HoldAll]; 
makeInertCode[code_] := 
    MapAll[myHold, Unevaluated[code], Heads -> True] 

Aquí está un ejemplo:

In[27]:= 
icd = makeInertCode[ 
     With[{x = RandomInteger[{1, 10}, 20]}, 
      Extract[x, Position[x, _?OddQ]]] 
     ] 

Out[27]= myHold[myHold[With][myHold[myHold[List][myHold[myHold[Set][myHold[x], 
myHold[myHold[RandomInteger][myHold[myHold[List][myHold[1],myHold[10]]],myHold[20]]]]]]], 
myHold[myHold[Extract][myHold[x], myHold[myHold[Position][myHold[x], myHold[myHold[ 
PatternTest][myHold[myHold[Blank][]], myHold[OddQ]]]]]]]]] 

Ahora podemos utilizar las herramientas de desestructuración estándar sin el peligro de evaluación prematura de código (para estar totalmente seguro, myHold se puede dar HoldAllComplete en lugar de HoldAll atributo):

In[28]:= Cases[icd, myHold[Extract][___], Infinity] 

Out[28]= {myHold[Extract][myHold[x], 
    myHold[myHold[Position][myHold[x], 
    myHold[myHold[PatternTest][myHold[myHold[Blank][]], 
    myHold[OddQ]]]]]]} 

una vez que el código se transforma/desestructurado, puede ser envuelto en 012.o HoldComplete, y luego las envolturas myHold se pueden eliminar, por ejemplo, mediante una regla como myHold[x___]:>x, aplicada repetidamente. Pero en general, el valor agregado de MapAll me parece bastante limitado, porque, en particular, es equivalente a Map con la especificación de nivel {0,Infinity}. No creo que se use con frecuencia.

+0

@Sjoerd: una aplicación donde esto es de hecho bastante útil es en la construcción de una diferencia de dos expresiones mma (que puede ser un código). Tal diff puede ser bastante útil, más que el estándar basado en cadenas, en el contexto de mma. Comencé con 'MapAll', pero terminé usando' MapIndexed' con '{0, Infinity}' para el diff, ya que también necesitaba saber el nivel de cada elemento. –

+0

Interesante. He estado pensando en una diferencia en el contexto de la pregunta de anotación de trama (http://stackoverflow.com/q/5744117/615464). ¿Podría hacer una diferencia entre una trama original y una trama con anotación y agregar la diferencia a una versión revisada de la primera? De esa manera, este método de guardar anotaciones sería más a prueba de futuro que el mío. –

+0

@Sjoerd La tarea que mencionas suena ambigua, ya que si bien puedo tomar una diferencia entre las dos imágenes, no veo una forma no ambigua de formular la "adición" al paso de la imagen transformada.Quizás, se puede hacer heurísticamente, pero dudo que sea robusto, a menos que se impongan restricciones adicionales al formato de la trama. –