ReplaceAll无法按预期工作

时间:2010-12-23 16:36:48

标签: wolfram-mathematica

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=2; b=1; h=1)中设置a,b和h的值,并且我按照预期得到了漂亮的双重心形。

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

我尝试阅读ReplaceAllParametricPlot,我发现最接近的线索是“ParametricPlot具有属性HoldAll并仅在分配后评估f变量的具体数值“没有多大帮助,或者我不会在这里。

感谢。

4 个答案:

答案 0 :(得分:4)

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

查看FullForm[temp1]以确认该表达式中没有ab

如果您将ReplaceAll设置为HoldFirst,则会阻止在ReplaceAll之前评估ParametricPlot,并且结果符合您的预期。在这种情况下,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,例如

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})

答案 1 :(得分:3)

在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作为属性,因此这并不能解释为什么ParametricPlot的{​​{1}}不同。 : - (

EDIT2:我使用HoldAll来查看会发生什么,似乎Trace只在最后才应用ReplaceAll变成了一个图形对象(并且不再包含a,b或h)。在ParametricPlot的情况下,保持评估为Hold[a] /. a -> 5,然后可以成功应用替换规则。

答案 2 :(得分:3)

这就是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  

现在,需要在您希望它运行的级别插入替换件。由于Plot的结果基本上是数字坐标已经“已解决”的图像,因此您需要在计算绘图之前将这些坐标放在中。在你的情况下:

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

答案 3 :(得分:1)

这不是一个答案,只是对使用带有绘图的模块的评论。

如果我按以下步骤进行

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,我发现对模块更直观)。

为了它的价值,我将使用替换规则生成原始参数图,如下所示:

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(编辑)不起作用(因为'Plot'将变量x视为本地,有效地使用Block'?)

在我看来,对于任何人来说都是绝对清楚的,即使是对Mathematica有基本知识的人,方法2的内容,显示了Mathematica的强大功能和易用性。当方程变得更复杂时,单独定义它们是有利的。现在不太清楚,必须使用方法3而不是方法1.(方法4,当然,可能是最好的。)