ReplaceAll 未按预期工作

发布于 2024-10-09 14:38:16 字数 1237 浏览 5 评论 0原文

Mathematica 还处于早期阶段,所以请原谅这个可能是一个非常明显的问题。我正在尝试生成一些参数图。我有:

ParametricPlot[{
    (a + b) Cos[t] - h Cos[(a + b)/b t],
    (a + b) Sin[t] - h Sin[(a + b)/b t]},
    {t, 0, 2 \[Pi]}, PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}

不高兴:不应用替换规则,并且 abh 保持未定义。

如果我这样做:

Hold@ParametricPlot[{
    (a + b) Cos[t] - h Cos[(a + b)/b t],
    (a + b) Sin[t] - h Sin[(a + b)/b t]},
    {t, 0, 2 \[Pi]}, PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}

看起来规则正在发挥作用,正如输出所证实的那样:

Hold[ParametricPlot[{(2 + 1) Cos[t] - 
1 Cos[(2 + 1) t], (2 + 1) Sin[t] - 1 Sin[(2 + 1) t]}, {t, 0, 
2 \[Pi]}, PlotRange -> All]]

这就是我所期望的。不过,如果取消 HoldParametricPlot 就不起作用。不过,方程或 ParametricPlot 本身没有任何问题,因为我尝试在单独的表达式中设置 a、b 和 h 的值 (a=2; b=1; h=1),我按预期得到了漂亮的双心形。

那么,我在 ReplaceAll 上做错了什么,为什么转换规则不起作用?这是 MMA 的另一个根本性重要方面,但我的 OOP 毁掉的大脑无法理解。

我尝试阅读 ReplaceAllParametricPlot,我发现的最接近的线索是“ParametricPlot 具有属性 HoldAll 并且仅在将特定数值分配给变量后才计算 f”,这没有多大帮助,否则我就不会在这里。

谢谢。

Still early days with Mathematica so please forgive what is probably a very obvious question. I am trying to generate some parametric plots. I have:

ParametricPlot[{
    (a + b) Cos[t] - h Cos[(a + b)/b t],
    (a + b) Sin[t] - h Sin[(a + b)/b t]},
    {t, 0, 2 \[Pi]}, PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}

No joy: the replacement rules are not applied and a, b and h remain undefined.

If I instead do:

Hold@ParametricPlot[{
    (a + b) Cos[t] - h Cos[(a + b)/b t],
    (a + b) Sin[t] - h Sin[(a + b)/b t]},
    {t, 0, 2 \[Pi]}, PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}

it looks like the rules ARE working, as confirmed by the output:

Hold[ParametricPlot[{(2 + 1) Cos[t] - 
1 Cos[(2 + 1) t], (2 + 1) Sin[t] - 1 Sin[(2 + 1) t]}, {t, 0, 
2 \[Pi]}, PlotRange -> All]]

Which is what I'd expect. Take the Hold off, though, and the ParametricPlot doesn't work. There's nothing wrong with the equations or the ParametricPlot itself, though, because I tried setting values for a, b and h in a separate expression (a=2; b=1; h=1) and I get my pretty double cardoid out as expected.

So, what am I doing wrong with ReplaceAll and why are the transformation rules not working? This is another fundamentally important aspect of MMA that my OOP-ruined brain isn't understanding.

I tried reading up on ReplaceAll and ParametricPlot and the closest clue I found was that "ParametricPlot has attribute HoldAll and evaluates f only after assigning specific numerical values to variables" which didn't help much or I wouldn't be here.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不寐倦长更 2024-10-16 14:38:16

Mathematica 通过首先评估每个子表达式的头来评估每个头而不保留属性。由于 ReplaceAll 没有保持属性,因此在替换之前 ParametricPlot 变为 Graphics

要查看表达式树,请执行

ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
      h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
    PlotRange -> All] /. {a -> 2, b -> 1, h -> 1} // Hold // TreeForm

相同

temp1=ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
          h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
        PlotRange -> All]
temp2={a -> 2, b -> 1, h -> 1} 
temp1/.temp2

从该树中您可以看到您的命令与执行Look 在 FullForm[temp1] 中确认该表达式中没有 ab

如果您将 ReplaceAll 设置为 HoldFirst,则会阻止 ParametricPlot 在 ReplaceAll 之前进行计算,结果就是您所期望的。在本例中,ReplaceAll 计算结果为带有头 ParametricPlot 的表达式,并且仅在该点计算 ParametricPlot。确保重置属性,因为更改内置命令的行为可能会产生意外的副作用。

SetAttributes[ReplaceAll, HoldFirst]; 
ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
    h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
  PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}
ClearAttributes[ReplaceAll, HoldFirst]

当需要使用 HoldAll 评估传递给函数的参数时,一个有用的技巧是对具有 List 头的表达式进行操作,并在结束,例如

ParametricPlot @@ ({{(a + b) Cos[t] - 
      h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, {t, 0,
      2 \[Pi]}, PlotRange -> All} /. {a -> 2, b -> 1, h -> 1})

Mathematica evaluates each head without holding attributes by first evaluating head of each subexpression. Since ReplaceAll doesn't have holding attributes, ParametricPlot becomes Graphics before replacement

To see the expression tree, do

ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
      h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
    PlotRange -> All] /. {a -> 2, b -> 1, h -> 1} // Hold // TreeForm

From that tree you can see that your command is the same as doing

temp1=ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
          h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
        PlotRange -> All]
temp2={a -> 2, b -> 1, h -> 1} 
temp1/.temp2

Look at FullForm[temp1] to confirm that there's no a or b in that expression.

If you set ReplaceAll to HoldFirst, that prevents ParametricPlot from being evaluated before ReplaceAll, and result is what you expected. In this case, ReplaceAll evaluates to expression with head ParametricPlot, and only at that point ParametricPlot is evaluated. Make sure to reset the attributes back because changing behavior of built-in commands can have unexpected side-effects.

SetAttributes[ReplaceAll, HoldFirst]; 
ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
    h Sin[(a + b)/b t]}, {t, 0, 2 \[Pi]}, 
  PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}
ClearAttributes[ReplaceAll, HoldFirst]

A useful trick when needing to evaluate arguments passed to function with HoldAll is to do operations on an expression with List head, and substitute ParametricPlot in the end, for instance

ParametricPlot @@ ({{(a + b) Cos[t] - 
      h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, {t, 0,
      2 \[Pi]}, PlotRange -> All} /. {a -> 2, b -> 1, h -> 1})
带上头具痛哭 2024-10-16 14:38:16

在 Mathematica 中使用局部变量的最佳方法是 Module[]

Module[{a = 2, b = 1, h = 1}, 
   ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All]]

这样 a、b 和 h 不会在全局上下文中获得指定值,而只能在 Module 内部获得指定值。如果您仍然想使用替换规则,则只需在完成替换后ReleaseHold

ReleaseHold[
   Hold@ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}]

编辑:至于为什么会发生这种情况。按照我的理解,HoldAll 可以防止函数的参数被任何规则(内部或显式)修改。您的 Hold 的作用是将整个函数(不仅仅是参数)置于保留状态,并且在函数经过评估(它没有执行)之后应用替换规则。所以仍然有一些东西需要替换)并且 HoldAll 不再有效。

In[1]  := Hold[a /. a -> 5]
Out[1] := Hold[a /. a -> 5]
In[2]  := Hold[a] /. a -> 5
Out[2] := Hold[5]

当然,Hold 也有 HoldAll 作为属性,所以这并不能解释为什么 ParametricPlotHoldAll是不同的。 :-(

EDIT2: 我使用 Trace 来查看发生的情况,似乎 ReplaceAll 仅在最后应用,当 < code>ParametricPlot 已经变成图形对象(并且不再包含 a、b 或 h)。保留的计算结果为 Hold[a],然后可以成功应用替换规则。

The best way for using local variables in Mathematica is Module[]:

Module[{a = 2, b = 1, h = 1}, 
   ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All]]

This way a, b, and h do not get assigned values in the Global context but only inside the Module. If you still want to use replacement rules you just have to ReleaseHold after you have done the replacement:

ReleaseHold[
   Hold@ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All] /. {a -> 2, b -> 1, h -> 1}]

EDIT: As to why this happens. The way I understand it, HoldAll prevents the arguments of the function from being modified by any rules (internal or explicit). What your Hold does, is place the entire function on hold (not just the arguments), and the replacement rule gets applied after the function has gone through evaluation (which it didn't so there is still something there to replace) and HoldAll is no longer valid.

In[1]  := Hold[a /. a -> 5]
Out[1] := Hold[a /. a -> 5]
In[2]  := Hold[a] /. a -> 5
Out[2] := Hold[5]

Of course, Hold also has HoldAll as an attribute, so this doen't explain why ParametricPlot's HoldAll is different. :-(

EDIT2: I used Trace to look at what happens, and it seems like ReplaceAll gets applied only at the very end, when ParametricPlot has already turned into a graphical object (and does not contain a, b, or h anymore). In the case of Hold[a] /. a -> 5 the hold evaluates to Hold[a] and the replacement rule can then be successfully applied.

等你爱我 2024-10-16 14:38:16

这就是 ReplaceAll 始终工作的方式。

请参见示例:

In[10]:= (a/a) /. a -> 0

Out[10]= 1  

显然,替换是在评估之后完成的,因为如果您这样做:

In[11]:= a = 0; a/a

During evaluation of In[11]:= Power::infy: Infinite expression 1/0 encountered. >>

During evaluation of In[11]:= Infinity::indet: Indeterminate expression 0 ComplexInfinity encountered. >>

Out[12]= Indeterminate  

现在,只需在您希望其操作的级别插入替换即可。由于绘图的结果基本上是一个数字坐标已“解决”的图像,因此您需要在计算绘图之前放入这些坐标。在您的情况下:

ParametricPlot[
   {(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]} 
   /. {a -> 2, b -> 1, h -> 1},
   {t, 0, 2 \[Pi]}, 

 PlotRange -> All
]

alt text

That is the way ReplaceAll always work.

See for example:

In[10]:= (a/a) /. a -> 0

Out[10]= 1  

Clearly the replacement is done AFTER the evaluation, because if you do:

In[11]:= a = 0; a/a

During evaluation of In[11]:= Power::infy: Infinite expression 1/0 encountered. >>

During evaluation of In[11]:= Infinity::indet: Indeterminate expression 0 ComplexInfinity encountered. >>

Out[12]= Indeterminate  

Now, is a matter of inserting the replacement at the level you want it to operate. As the result of a Plot is basically an Image with the numeric coordinates already "solved", you want to put those coordinates in before the plot is calculated. In your case:

ParametricPlot[
   {(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]} 
   /. {a -> 2, b -> 1, h -> 1},
   {t, 0, 2 \[Pi]}, 

 PlotRange -> All
]

alt text

故事↓在人 2024-10-16 14:38:16

这不是一个答案,只是对使用 Module with Plot 的评论。

如果我按如下方式进行,则

f[t_] := {(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
   h Sin[(a + b)/b t]}

以下内容不起作用

方法 1:

Module[{a = 2, b = 1, h = 1}, 
 ParametricPlot[f[t], {t, 0, 2 \[Pi]}, PlotRange -> All]]

方法 2:

Module[{a = 2, b = 1, h = 1}, 
 ParametricPlot[Evaluate[f[t]], {t, 0, 2 \[Pi]}, PlotRange -> All]]

以下内容确实有效(方法 3)

ParametricPlot[
 Module[{a = 2, b = 1, h = 1}, Evaluate[f[t]]], {t, 0, 2 \[Pi]}, 
 PlotRange -> All]

,与上述方法(方法 4)一样有效

Module[{a = 2, b = 1, h = 1}, 
   ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All]]

任何人都可以解释为什么方法 4 有效,但方法 2 无效? (这同样适用于 With,我发现它对 Module 来说更直观)。

就其价值而言,我将使用替换规则生成原始参数图,如下所示:

ParametricPlot[
 Evaluate[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
     h Sin[(a + b)/b t]}] /. {a -> 2, b -> 1, h -> 1}, {t, 0, 
  2 \[Pi]}, PlotRange -> All]

编辑

f[x_] := (a x)/(b + x);
With[{a = 10, b = 100}, Plot[Evaluate[f[x]], {x, 0, 100}]]
With[{a = 10, b = 100}, Plot[(a x)/(b + x), {x, 0, 100}]]
Plot[With[{a = 10, b = 100}, Evaluate[f[x]]], {x, 0, 100}]
Plot[Evaluate[f[x]] /. {a -> 10, b -> 100}, {x, 0, 100}]

方法 1(编辑)不起作用(因为“绘图”将变量 x 视为局部变量,有效地使用“块”?)

在我看来任何人,甚至是那些对 Mathematica 有基本了解的人,都绝对清楚方法 2 发生了什么,显示了 Mathematica 的强大功能和易用性。当方程变得更加复杂时,单独定义它们是否有利?现在还不清楚是否必须使用方法 3 来代替方法 1。(当然,方法 4 可能是最好的。)

This is not an answer as such, just a comment on using Module with Plot.

If I proceed as follows

f[t_] := {(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
   h Sin[(a + b)/b t]}

The following will NOT work

Method 1:

Module[{a = 2, b = 1, h = 1}, 
 ParametricPlot[f[t], {t, 0, 2 \[Pi]}, PlotRange -> All]]

Method 2:

Module[{a = 2, b = 1, h = 1}, 
 ParametricPlot[Evaluate[f[t]], {t, 0, 2 \[Pi]}, PlotRange -> All]]

The following does work (Method 3)

ParametricPlot[
 Module[{a = 2, b = 1, h = 1}, Evaluate[f[t]]], {t, 0, 2 \[Pi]}, 
 PlotRange -> All]

as does the method described above (method 4)

Module[{a = 2, b = 1, h = 1}, 
   ParametricPlot[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - h Sin[(a + b)/b t]}, 
   {t, 0, 2 \[Pi]}, 
   PlotRange -> All]]

Can anyone explain why method 4 works but method 2 doesn't? (The same applies to With, which I find more intuitive to Module).

For what its worth, I would generate the original parametric plot using replacement rules as follows:

ParametricPlot[
 Evaluate[{(a + b) Cos[t] - h Cos[(a + b)/b t], (a + b) Sin[t] - 
     h Sin[(a + b)/b t]}] /. {a -> 2, b -> 1, h -> 1}, {t, 0, 
  2 \[Pi]}, PlotRange -> All]

EDIT

f[x_] := (a x)/(b + x);
With[{a = 10, b = 100}, Plot[Evaluate[f[x]], {x, 0, 100}]]
With[{a = 10, b = 100}, Plot[(a x)/(b + x), {x, 0, 100}]]
Plot[With[{a = 10, b = 100}, Evaluate[f[x]]], {x, 0, 100}]
Plot[Evaluate[f[x]] /. {a -> 10, b -> 100}, {x, 0, 100}]

Method 1 (of Edit) does not work (because 'Plot' treats the variable x as local, effectively using Block'?)

It seems to me that it is absolutely clear to anyone, even those with a rudimentary knowledge of Mathematica, what is going on with Method 2, showing the power and ease-of-use of Mathematica. When the equations become more complex, is it advantageous to define them separately. It is now not so clear that Method 3 must be used instead of Method 1. (Method 4, of course, is probably the best of all.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文