2010-04-28 12 views
7

El código siguiente devuelve 14 como era de esperar:ámbito léxico y dinámica en Mathematica: Las variables locales con el módulo, con, y el bloque

Block[{expr}, 
    expr = 2 z; 
    f[z_] = expr; 
    f[7]] 

Pero si cambia a un Block que Module entonces se vuelve 2*z . Parece que no importa qué otras variables además de expr localizas. Pensé que entendía Módulo, Bloque y Con en Mathematica, pero no puedo explicar la diferencia de comportamiento entre Módulo y Bloque en este ejemplo.

recursos relacionados:

PS: Gracias a Michael Pilat, Davorak y Bill White para la siguiente t el rastro de olor en esta rareza. Davorak aclara y llega al meollo del problema aquí: Why would Mathematica break normal scoping rules in Module?

Respuesta

6

Yo también estaba un poco sorprendido por esto, pero no creo que sea un error. Si se mira profundamente en los ejemplos en el reference page for Module, bajo la sección titulada Problemas Posibles, hay una pequeña nota que dice "Variables se renombran de los ámbitos anidados" y da el siguiente ejemplo:

In[1]:= Module[{e = Expand[(1 + x)^5]}, Function[x, e]] 

Out[1]= Function[x$, e$1194] 

In[2]:= %[10] 

Out[2]= 1 + 5 x + 10 x^2 + 10 x^3 + 5 x^4 + x^5 

Function es otro constructo de scoping como Module, por lo que x se renombra internamente como x$ en el alcance del Function, similar a lo que descubrió con Trace sobre .

En su Module definir f, Set es otro de tales constructos de alcance, y por lo tanto se cambia el nombre z cuando f se define dentro de un Module, pero no cuando se está dentro de un Block. Siguiendo el consejo de que el ejemplo de la documentación Module, se puede construir el lado derecho de su función a partir de sus partes para evitar el cambio de nombre léxica del alcance anidado:

In[3]:= Clear[f, z] 

In[4]:= Module[{expr}, 
    expr = 2 z; 
    Set @@ {f[z_], expr}; 
    f[7]] 

Out[4]= 14 

HTH!

+1

Guau, ¡no es lo que yo llamaría una bonita solución para el trabajo, pero es impresionante saberlo! – dreeves

+0

Es por eso que vuelvo a SO todos los días. Me aprende algo: **. – Timo

+0

Prefiero tomar el golpe de rendimiento inesperado de tener expr envuelto en una conversión alfa perezosa en lugar de lidiar con la sintaxis inconsistente. ¿Qué consideraciones de diseño del lenguaje están entrando para jugar aquí? – Davorak

2

¿Ha usado Trace en ambas expresiones?

+0

¡Ah, listo! Así que 'z_' se está convirtiendo en' z $ _' en la versión del Módulo. Así que supongo que eso lo explica, pero todavía no entiendo por qué está sucediendo eso. Quizás actualizaré la pregunta para preguntar sobre esa rareza específicamente. – dreeves

3

En primer lugar creo que ha expuesto un error aquí.

En segundo lugar, creo que puedo ofrecer una idea de por qué sucede esto, teniendo en cuenta que mi conocimiento de los aspectos internos de las matemáticas es limitado.

Una declaración como: f [z_]: = 2 z en forma completa es:

SetDelayed[f[Pattern[z, Blank[]]], 2 z] 

Esto establece el DownValue [f] a:

{HoldPattern[f[z_]] :> 2 z} 

Luego, más tarde cuando una expresión, como f [2], es posteriormente se evalúa algo así como que se está preformado siguientes:

f[2] /. HoldPattern[f[z_]] :> 2 z 

cual sería evaluar a 4. Ahora bien, esto es todo lo po Es posible porque la coincidencia de patrones está ocurriendo con el Patrón [z, Blanco []] del primer bloque de códigos. Esto funciona incluso si ha establecido perviously z en un número. En otras palabras.

z = 5; 
f[z_] := 2*z 

todavía produce los mismos downvalues ​​para f:

{HoldPattern[f[z_]] :> 2 z} 

Esto es posible porque patrón tiene el atributo HoldFirst.

El atributo HoldFirst no es suficiente protección si evalúa esto dentro de un Módulo. Ejemplo:

SetAttributes[tmp, HoldFirst]; 
Module[{expr}, 
expr = 2 z; 
tmp[expr] 
] 

salidas:

tmp[expr$8129] 

propongo porque HoldFirst Atributo no proporciona inmunidad a la regla de reescritura variables de módulo que cualquier patrón en una regla que contiene una variable local han reescrito sus variables del patrón . sim-> Símbolo [SymbolName [SYM] ~~ "$"]

Module[{expr}, 
Hold[z_ -> (z; expr)] 
] 
(*Hold[z$_ -> (z$; expr$1391)]*) 

z ha ser reescrito en ambos lados de la regla en una simple conversión alfa.

Si la regla no contiene una variable local sin reescritura sucede:

Module[{expr}, 
Hold[z_ -> (z)] 
] 
(*Hold[z_ -> z]*) 

En lugar de buscar para ver si una variable local coincida con una variable regla se aplica la regla de la manta por encima.

Así que el problema es que el expr local no se evalúa antes de que tenga lugar la conversión alfa. O quizás sería aún mejor tener expr envuelto en una conversión alfa evaluada de forma perezosa que se requeriría para RuleDelayed.

Esto no ocurre en Bloque porque el Bloque no reescribe ninguna de las variables locales.

¿Alguna otra idea? ¿Alguien ve algún agujero en mi lógica?

+0

Muchas gracias, Davorak. Parece que tú y Michael Pilat están teniendo el mismo problema, ¿verdad? – dreeves

+0

Es el mismo problema. La conversión alfa le da alcance a Función, Regla, Conjunto, SetDelayed, pero las variables reescritas todavía están en el espacio de nombres global. Supongo que estaba equivocado sobre si era un error, pero es bastante molesto. – Davorak

Cuestiones relacionadas