使用 With 绘图与使用 Block 绘图 (Mathematica) [英] Plot using With versus Plot using Block (Mathematica)

查看:48
本文介绍了使用 With 绘图与使用 Block 绘图 (Mathematica)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想描述一个我在 Plot 中遇到的问题,使用 With 来保持定义的参数本地".我不一定要求解决:我遇到的问题是理解问题.

I want to describe an issue I have been having with Plot using With to keep defined parameters 'local'. I am not necessarily asking for a fix: the problem I have is one of understanding.

有时我会使用如下结构来获取 Plot:

Sometimes I use a construction such as the following to obtain a Plot:

方法一

plot1 = With[{vmax = 10, km = 10}, 
  Plot[Evaluate@((vmax x)/(km + x)), {x, 0, 100}, 
   AxesOrigin -> {0, 0}]]

我喜欢这种方法,即使是非Mathematica 用户也很清楚到底发生了什么.

I like this method, and it is reasonably clear even to non-Mathematica users exactly what is going on.

当要绘制的方程变得更加复杂时,我喜欢在图的外部定义它们(使用 SetDelayed).例如:

When the equations to be plotted become more complex, I like to define them external to the plot (using SetDelayed). For example:

f[x_] := (vmax x)/(km + x)

但是,以下不起作用

方法二

plot2 = With[{vmax = 10, km = 10}, 
  Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0}]]

我一直天真地认为它应该.但是,根据帮助声明,

I have always naively thought that it should. However, based on the Help statement that

Plot 将变量 x 视为局部变量,有效地使用 Block

Plot treats the variable x as local, effectively using Block

我使用了各种解决方法,主要类似于以下内容

I have used various workarounds, mostly something like the following

方法三

plot3 = Plot[With[{vmax = 10, km = 10}, Evaluate@f[x]], {x, 0, 100}, 
  AxesOrigin -> {0, 0}]

这看起来很尴尬,通常甚至对Mathematica 用户都需要进一步解释.

This one seems very awkward, and usually requires further explanation even to Mathematica users.

绘制输出

然而,最近我偶然发现在方法 2 中用 Block 代替 With 完全符合预期.

However, recently I found out by chance that substituting Block for With in Method 2 works exactly as expected.

例如,我可以执行以下操作(在我看来这是一种非常通用的方法):

I can, for example, do something like the following (which to me seems a very versatile approach):

plot4 = Block[{vmax = {10, 10, 10}, km = { 10, 100, 1000}}, 
  Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0}, 
   PlotStyle -> {Red, Green, Blue}]]

给予

我的问题如下.方法 1 和方法 2 中 With 的行为差异的解释是什么?我应该期望方法 2 不起作用吗?此外,对于方法 2 中 BlockWith 的行为差异的解释是什么?我应该能够预测 Block 会起作用吗?

My questiions are as follows. What is the explanation for the difference in behaviour with With in Method 1 and 2? Should I have expected Method 2 not to work? Furthermore, what is the explanation for the difference in behaviour with Block and With in Method 2? Should I have been able to predict that Block would work?

有趣的是,那些比我更有经验的人向我提出了许多解决方法,但没有人建议使用 Block.

Funnily enough many workarounds have been suggested to me by those more experienced than I, but nobody suggested using Block.

最后,我需要将 vmaxkm 保持在本地.(它们已在其他地方以代数方式定义)

Finally, I need to keep vmax and km local.(They have been defined algebraically elsewhere)

推荐答案

您的问题不是关于 Plot,而是关于范围构造如何工作.这里的主要混淆是由于词法范围和动态范围之间的差异.罪魁祸首是这个定义:

Your question is not so much about Plot as it is about how the scoping constructs work. The main confusion here is due to the differences between lexical and dynamic scoping. And the main culprit is this definition:

f[x_] := (vmax x)/(km + x)

它的问题在于它使 f 隐式依赖于全局符号(变量)vmaxkm.我非常反对这种结构,因为它们会导致无限的混乱.现在,可以通过以下示例说明发生的情况:

The problem with it is that it makes f implicitly depend on the global symbols (variables) vmax and km. I am very much against this sort of constructs since they lead to infinite confusion. Now, what happens can be illustrated with the following example:

In[55]:= With[{vmax =1, km = 2},f[x]]

Out[55]= (vmax x)/(km+x)

要理解为什么会发生这种情况,必须了解词法范围的含义.我们知道 With 有一个 HoldAll 属性.它的工作方式是,它看起来就是它内部的字面,并用声明列表中的值替换在正文中字面发现的变量.这发生在变量绑定阶段,然后才让主体进行评估.由此可以看出,以下操作很明显:

To understand why this happens, one must understand what the lexical scoping means. We know that With has a HoldAll attribute. The way it works is that it looks are what is literally inside it, and substitutes variables found literally in the body with their values from the declaration list. This happens during the variable-binding stage, and only then it lets the body to evaluate. From this, it is clear that the following will work:

In[56]:= With[{vmax =1, km = 2},Evaluate[f[x]]]

Out[56]= x/(2+x) 

这是有效的,因为 Evaluate 覆盖了 WithHoldAll 属性的部分",强制主体在其他任何事情之前进行评估(变量绑定,以及随后的身体评估).因此,完全等同于使用上面的 With[{vmax = 1, km = 2}, (vmax x)/(km + x)],正如您在 中看到的跟踪.谜题的下一部分是为什么

This worked because Evaluate overrides the "part" of HoldAll attribute of With, forcing the body to evaluate before anything else (variable binding, and subsequent body evaluation). Therefore, it would be completely equivalent to use just With[{vmax = 1, km = 2}, (vmax x)/(km + x)] above, as you can see with Trace. The next part of the puzzle is why

With[{vmax = 10, km = 10}, Plot[Evaluate@f[x], {x, 0, 100}, AxesOrigin -> {0, 0}]]

不起作用.这是因为这次我们先评估主体.Evaluate 的存在只影响 Plot 内部的 f[x],而不影响 Plot 内部 Plot 本身的评估代码>与.

does not work. This is because this time we do not evaluate the body first. The presence of Evaluate affects only f[x] inside Plot, but not the evaluation of Plot itself inside With. This is illustrated by

In[59]:= With[{vmax = 10, km = 10}, q[Evaluate@f[x]]]

Out[59]= q[(vmax x)/(km + x)]

此外,我们不希望 Plot 先求值,因为这样 vmaxkm 的值将不会被定义.然而,With 看到的只是f[x],并且由于参数vmaxkm 不是字面存在于那里(词汇范围,记住),不会进行替换.我们应该在这里使用 Block 吗,事情会起作用,因为 Block 使用动态范围,这意味着它及时重新定义值(如果你愿意,它是执行堆栈的一部分),而不是比到位.因此,使用 Block[{a =1, b =2}, ff[x]] 其中 ff 隐式依赖于 a>b(大致)等价于 a=1;b=2;ff[x](区别在于 abBlock 范围离开后恢复它们的全局值).所以,

Moreover, we do not want Plot to evaluate first, since then the values of vmax and km will not be defined. However, all that With sees is f[x], and since the parameters vmax and km are not literally present in there (lexical scoping, remember), no substitution will be made. Should we use Block here, and things will work, because Block uses dynamic scoping, meaning that it redefines values in time (part of the execution stack if you wish), rather than in place. Therefore, using Block[{a =1, b =2}, ff[x]] where ff implicitly depends on a and b is (roughly) equivalent to a=1;b=2;ff[x] (with the difference that a and b resume their global values after the Block scope is left). So,

In[60]:= Block[{vmax = 10, km = 10}, q[Evaluate@f[x]]]

Out[60]= q[(10 x)/(10 + x)]

要使 With 版本正常工作,您必须为 f[x] (r.h.s) 注入表达式,例如:

To make the With version work, you'd have to inject the expression for f[x] (r.h.s), for example like so:

In[63]:= Unevaluated[With[{vmax = 10, km = 10}, q[f[x]]]] /. DownValues[f]

Out[63]= q[(10 x)/(10 + x)]

请注意,这是行不通的:

Note that this won't work:

In[62]:= With[{fx = f[x]}, With[{vmax = 10, km = 10},  q[fx]]]

Out[62]= q[(vmax x)/(km + x)]

但这里的原因很微妙:虽然外部 With 在内部评估之前计算,但它发现变量名冲突并重命名其变量.规则更具破坏性,它们不尊重内部范围结构.

But the reason here is quite subtle: while outer With evaluates before the inner one, it spots the variable name conflicts and renames its variables. Rules are much more disruptive, they don't respect inner scoping constructs.

编辑

如果坚持嵌套With-s,那么可以这样欺骗With的名称冲突解决机制并使其工作:

If one insists on nested With-s, here is how one can fool the name conflict resolution mechanism of With and make it work:

In[69]:= With[{fx = f[x]}, With @@ Hold[{vmax = 10, km = 10}, q[fx]]]

Out[69]= q[(10 x)/(10 + x)]

由于外部 With 不再能检测到内部 With 的存在(使用 Apply[With,Hold[...]] 使得内部 With 有效地动态生成),它不会进行任何重命名,然后它就可以工作了.当您不想重命名时,这是一个欺骗词法范围名称解析机制的通用技巧,尽管必须使用它通常表明设计很糟糕.

Since outer With can no longer detect the presence of inner With (using Apply[With,Hold[...]] makes the inner With effectively dynamically generated), it does not make any renamings, and then it works. This is a general trick to fool lexical scoping name resolution mechanism when you don't want renaming, although the necessity to use it usually indicates a bad design.

结束编辑

但我跑题了.总而言之,使您的第二种方法工作非常困难,并且需要非常奇怪的构造,例如

But I digressed. To summarize, making your second method work is quite hard and requires really weird constructs like

Unevaluated[ With[{vmax = 10, km = 10}, Plot[Evaluate@f[x], {x, 0, 100},
     AxesOrigin -> {0, 0}]]] /.  DownValues[f]

With[{fx = f[x]}, 
   With @@ Hold[{vmax = 10, km = 10}, 
       Plot[Evaluate@fx, {x, 0, 100}, AxesOrigin -> {0, 0}]]]

再说一遍:所有这一切都是因为 With 必须在代码中明确看到"变量,才能进行替换.相比之下,Block 不需要那样,它会在评估时动态替换值,基于它们修改后的全局值,就像您进行了赋值一样,这就是它起作用的原因.

Once again: all this is because With must "see" the variables explicitly in the code, to make replacements. In contrast, Block does not need that, it replaces values dynamically at the moment of evaluation, based on their modified global values, as if you made assignments, this is why it works.

现在,真正的罪魁祸首是您对 f 的定义.如果您使用显式参数传递定义了 f,您就可以避免所有这些麻烦:

Now, the real culprit is your definition of f. You could have avoided all these troubles should you have defined your f with explicit parameter-passing:

ff[x_, vmax_, km_] := (vmax x)/(km + x)

现在,这是开箱即用的:

Now, this works out of the box:

With[{vmax = 10, km = 10}, 
   Plot[Evaluate@ff[x, vmax, km], {x, 0, 100}, AxesOrigin -> {0, 0}]]

因为参数明确存在于函数调用签名中,因此对 With 可见.

because the parameters are explicitly present in the function call signature, and so are visible to With.

总结:您观察到的是词汇和动态范围之间相互作用的结果.词法范围构造必须在变量绑定阶段(评估之前)在代码中明确看到"它们的变量,否则它们将无效.动态范围有效地修改了符号的,并且在这个意义上要求不高(你付出的代价是使用大量动态范围的代码更难理解,因为它混合了状态和行为).出现问题的主要原因是函数定义对全局符号(不在函数的形式参数列表中)进行了隐式依赖.最好避免这种结构.仍然可以使事情工作,但这要复杂得多(如上所述),并且至少对于手头的情况,没有充分的理由.

To summarize: what you observed is a consequence of the interplay between lexical and dynamic scoping. Lexical scoping constructs must "see" their variables explicitly in the code at the variable-binding stage (before evaluation), or they won't be effective. Dynamic scoping effectively modifies values of symbols, and is in this sense less demanding (the price you pay is that the code using lots of dynamic scoping is harder to understand, since it mixes state and behavior). The main reason for trouble is the function definition that makes implicit dependencies on global symbols (that are not in the formal parameter list of the function). It is best to avoid such constructs. It is still possible to make things work, but this is considerably more complicated (as was demonstrated above), and, at least for the case at hand, for no good reason.

这篇关于使用 With 绘图与使用 Block 绘图 (Mathematica)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆