wolfram-mathematica - vertical - graficas 3d mathematica
Trazar utilizando With versus Trazar usando Block(Mathematica) (2)
Quiero describir un problema que he tenido con Plot
usando With
para mantener los parámetros definidos como "locales". No estoy necesariamente pidiendo una solución: el problema que tengo es de comprensión.
A veces utilizo una construcción como la siguiente para obtener una parcela:
Método 1
plot1 = With[{vmax = 10, km = 10},
Plot[Evaluate@((vmax x)/(km + x)), {x, 0, 100},
AxesOrigin -> {0, 0}]]
Me gusta este método, y es bastante claro incluso para los usuarios que no son Mathematica exactamente lo que está sucediendo.
Cuando las ecuaciones a ser dibujadas se vuelven más complejas, me gusta definirlas externas a la gráfica (usando SetDelayed). Por ejemplo:
f[x_] := (vmax x)/(km + x)
Sin embargo, lo siguiente no funciona.
Método 2
plot2 = With[{vmax = 10, km = 10},
Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0}]]
Siempre he pensado ingenuamente que debería. Sin embargo, basado en la declaración de ayuda que
La trama trata la variable x como local, utilizando efectivamente el bloque
He utilizado varias soluciones, sobre todo algo como lo siguiente
Método 3
plot3 = Plot[With[{vmax = 10, km = 10}, Evaluate@f[x]], {x, 0, 100},
AxesOrigin -> {0, 0}]
Este parece muy incómodo y, por lo general, requiere una explicación adicional incluso para los usuarios de Mathematica .
Trazar salidas
Sin embargo, recientemente descubrí por casualidad que sustituir Block
por With
en el Método 2 funciona exactamente como se esperaba.
Por ejemplo, puedo hacer algo como lo siguiente (lo que me parece un enfoque muy versátil):
plot4 = Block[{vmax = {10, 10, 10}, km = { 10, 100, 1000}},
Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0},
PlotStyle -> {Red, Green, Blue}]]
dando
Mis preguntas son las siguientes. ¿Cuál es la explicación de la diferencia de comportamiento con With
en los Métodos 1 y 2? ¿Debería haber esperado que el Método 2 no funcionara? Además, ¿cuál es la explicación de la diferencia de comportamiento con Block
y With
en el Método 2? ¿Debería haber podido predecir que Block
funcionaría?
Curiosamente, muchos expertos me han sugerido varias soluciones alternativas, pero nadie sugirió usar Block
.
Finalmente, necesito mantener vmax
y km
local. (Se han definido algebraicamente en otros lugares)
Solo dos comentarios:
usando Block, no necesitas usar Evaluate. Es decir, el Bloque [{vmax = 10, km = 2}, Gráfico [f [x], {x, 0, 100}] funcionará.
Otra forma de hacer esto es definir reglas de sustitución: regla = {vmax -> 10, km -> 10}; Parcela [f [x] /. regla, {x, 0, 100}] La ventaja es que puede reutilizar la regla en otras declaraciones.
Su pregunta no es tanto sobre Plot
como sobre cómo funcionan las construcciones de alcance. La principal confusión aquí se debe a las diferencias entre el alcance léxico y el dinámico. Y el principal culpable es esta definición:
f[x_] := (vmax x)/(km + x)
El problema con esto es que hace que f
dependa implícitamente de los símbolos globales (variables) vmax
y km
. Estoy muy en contra de este tipo de construcciones, ya que conducen a una confusión infinita. Ahora, lo que sucede se puede ilustrar con el siguiente ejemplo:
In[55]:= With[{vmax =1, km = 2},f[x]]
Out[55]= (vmax x)/(km+x)
Para entender por qué sucede esto, uno debe entender qué significa el alcance léxico . Sabemos que With
tiene un atributo HoldAll
. La forma en que funciona es que parece que está literalmente dentro, y sustituye las variables que se encuentran literalmente en el cuerpo con sus valores de la lista de declaración. Esto sucede durante la etapa de vinculación variable, y solo entonces permite que el cuerpo evalúe. A partir de esto, queda claro que lo siguiente funcionará:
In[56]:= With[{vmax =1, km = 2},Evaluate[f[x]]]
Out[56]= x/(2+x)
Esto funcionó porque Evaluate
anula la "parte" del atributo HoldAll
de With
, lo que obliga al cuerpo a evaluar antes que cualquier otra cosa (vinculación de variables y posterior evaluación del cuerpo). Por lo tanto, sería completamente equivalente usar solo With[{vmax = 1, km = 2}, (vmax x)/(km + x)]
arriba, como puede ver con Trace
. La siguiente parte del rompecabezas es por qué
With[{vmax = 10, km = 10}, Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0}]]
No funciona. Esto es porque esta vez no evaluamos el cuerpo primero. La presencia de Evaluate
solo afecta a f[x]
dentro de Plot
, pero no a la evaluación de Plot
sí dentro de. Esto es ilustrado por
In[59]:= With[{vmax = 10, km = 10}, q[Evaluate@f[x]]]
Out[59]= q[(vmax x)/(km + x)]
Además, no queremos que Plot
evalúe primero, ya que los valores de vmax
y km
no se definirán. Sin embargo, todo lo que With
see es f[x]
, y como los parámetros vmax
y km
no están literalmente presentes allí (alcance léxico, recuerde), no se realizará ninguna sustitución. Deberíamos usar el Block
aquí, y las cosas funcionarán, porque el Block
utiliza el alcance dinámico, lo que significa que redefine los valores en el tiempo (parte de la pila de ejecución si lo desea), en lugar de hacerlo en su lugar. Por lo tanto, al usar el Block[{a =1, b =2}, ff[x]]
donde ff
depende implícitamente de a
y b
es (aproximadamente) equivalente a a=1;b=2;ff[x]
(con la diferencia que b
reanudan sus valores globales después de que se deja el alcance del Block
). Asi que,
In[60]:= Block[{vmax = 10, km = 10}, q[Evaluate@f[x]]]
Out[60]= q[(10 x)/(10 + x)]
Para hacer que funcione la versión With
, deberías inyectar la expresión para f[x]
(rhs), por ejemplo, así:
In[63]:= Unevaluated[With[{vmax = 10, km = 10}, q[f[x]]]] /. DownValues[f]
Out[63]= q[(10 x)/(10 + x)]
Tenga en cuenta que esto no funcionará:
In[62]:= With[{fx = f[x]}, With[{vmax = 10, km = 10}, q[fx]]]
Out[62]= q[(vmax x)/(km + x)]
Pero la razón aquí es bastante sutil: mientras externo With
evalúa antes que el interno, detecta los conflictos de nombre de variable y renombra sus variables. Las reglas son mucho más disruptivas, no respetan las construcciones de alcance interno.
EDITAR
Si uno insiste en anidados With
-s, aquí es cómo se puede engañar al mecanismo de resolución de conflictos de nombres de With
y hacer que funcione:
In[69]:= With[{fx = f[x]}, With @@ Hold[{vmax = 10, km = 10}, q[fx]]]
Out[69]= q[(10 x)/(10 + x)]
Dado que la opción " With
externo ya no puede detectar la presencia de " With
interno (con Apply[With,Hold[...]]
hace que la With
interna se genere de forma efectiva), no hace ningún cambio de nombre y, a continuación, funciona. Este es un truco general para engañar al mecanismo de resolución de nombres de ámbito léxico cuando no desea cambiar el nombre, aunque la necesidad de usarlo generalmente indica un mal diseño.
EDICIÓN FINAL
Pero me desvié. Para resumir, hacer que tu segundo método funcione es bastante difícil y requiere construcciones realmente extrañas como
Unevaluated[ With[{vmax = 10, km = 10}, Plot[Evaluate@f[x], {x, 0, 100},
AxesOrigin -> {0, 0}]]] /. DownValues[f]
o
With[{fx = f[x]},
With @@ Hold[{vmax = 10, km = 10},
Plot[Evaluate@fx, {x, 0, 100}, AxesOrigin -> {0, 0}]]]
Una vez más: todo esto se debe a que With
debe "ver" las variables explícitamente en el código, para hacer reemplazos. En contraste, Block
no necesita eso, reemplaza los valores dinámicamente en el momento de la evaluación, en función de sus valores globales modificados, como si hiciera asignaciones, es por eso que funciona.
Ahora, el verdadero culpable es tu definición de f
. Podría haber evitado todos estos problemas si hubiera definido su f
con el paso explícito de parámetros:
ff[x_, vmax_, km_] := (vmax x)/(km + x)
Ahora, esto funciona fuera de la caja:
With[{vmax = 10, km = 10},
Plot[Evaluate@ff[x, vmax, km], {x, 0, 100}, AxesOrigin -> {0, 0}]]
porque los parámetros están presentes explícitamente en la función de firma de llamada, y por lo tanto son visibles para With
.
Para resumir: lo que observaste es una consecuencia de la interacción entre el alcance léxico y el dinámico. Las construcciones de alcance léxico deben "ver" sus variables explícitamente en el código en la etapa de vinculación de variables (antes de la evaluación), o no serán efectivas. El alcance dinámico modifica de manera efectiva los valores de los símbolos, y en este sentido es menos exigente (el precio que paga es que el código que usa un gran alcance del alcance dinámico es más difícil de entender, ya que combina estado y comportamiento). La razón principal de los problemas es la definición de la función que crea dependencias implícitas en los símbolos globales (que no están en la lista de parámetros formales de la función). Es mejor evitar tales construcciones. Todavía es posible hacer que las cosas funcionen, pero esto es considerablemente más complicado (como se demostró anteriormente) y, al menos para el caso que nos ocupa, sin ninguna buena razón.