ReplaceAll не работает должным образом

Все еще первые дни с 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}

Без радости: правила замены не применяются и a, b и h остаются неопределенными.

Если я вместо этого сделаю:

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]]

Чего я и ожидал. Уберите Hold, а ParametricPlot не работает. Тем не менее, нет ничего плохого в уравнениях или самом ParametricPlot, потому что я попытался установить значения для a, b и h в отдельном выражении (a=2; b=1; h=1), и я получил свой симпатичный двойной кардоид, как и ожидалось.

Итак, что я делаю не так с ReplaceAll и почему не работают правила трансформации? Это еще один фундаментально важный аспект ММА, который мой изуродованный ООП мозг не понимает.

Я попытался прочитать ReplaceAll и ParametricPlot, и самая близкая подсказка, которую я нашел, заключалась в том, что «ParametricPlot имеет атрибут HoldAll и оценивает f только после присвоения конкретных числовых значений переменным», что не очень помогло, иначе меня бы здесь не было.

Спасибо.


person Tim    schedule 23.12.2010    source источник
comment
Ха! Когда у вас есть молоток, любая проблема начинает казаться гвоздем.   -  person Tim    schedule 24.12.2010


Ответы (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], чтобы убедиться, что в этом выражении нет a или b.

Если вы установите 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 в конце, например

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})
person Yaroslav Bulatov    schedule 23.12.2010
comment
Спасибо за понятное и подробное объяснение. Я приму это, поскольку вы зашли немного дальше, чем Велисарий, но я ценю и то, и другое. - person Tim; 24.12.2010

Лучший способ использования локальных переменных в системе 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}]

EDIT: почему это происходит. Насколько я понимаю, 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 в качестве атрибута, так что это не объясняет, почему HoldAll ParametricPlot отличается. :-(

EDIT2: я использовал Trace, чтобы посмотреть, что происходит, и кажется, что ReplaceAll применяется только в самом конце, когда ParametricPlot уже превратился в графический объект (и не содержит a, b или ч больше). В случае Hold[a] /. a -> 5 удержание оценивается как Hold[a], после чего можно успешно применить правило замены.

person Timo    schedule 23.12.2010
comment
Спасибо. Я знал о Module, но очень хотел бы знать, почему здесь не работает /.. - person Tim; 23.12.2010
comment
Я предпочитаю With[] вместо Module[] для таких вещей (я резервирую Module[] и Block[] для рутины), но я думаю, что это стилистический момент... - person ; 27.12.2010

Так всегда работает 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
]

альтернативный текст

person Dr. belisarius    schedule 23.12.2010
comment
Ах да, я вижу, это имеет смысл. Теперь мне все ясно, когда я прочитал это вместе с подробным объяснением Ярослава. Надо будет подумать кого сюда принять :) - person Tim; 24.12.2010
comment
@Tim Не стесняйтесь принимать все, что хотите. Хорошо бы прийти к хорошему и полному ответу в целом. - person Dr. belisarius; 24.12.2010

Это не ответ как таковой, а просто комментарий к использованию модуля с сюжетом.

Если я поступлю следующим образом

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, пожалуй, лучший из всех.)

person tomd    schedule 23.12.2010
comment
a,b,c, которые вы определяете в Module[{a,b,c},, являются другими переменными, чем те, которые вы определяете за пределами Конструкция модуля. Вот почему это не работает: обзор - person Dr. belisarius; 24.12.2010
comment
@belisarius Спасибо за ваш комментарий. Я знаю, что переменные различаются при определении в модуле (и я знаю об области видимости), но я не понимаю, почему это имеет значение. Или, по крайней мере, я не понимаю, почему. Наверняка речь идет о поведении Plot? Я (сейчас) прочитал в справке, что «Plot рассматривает переменную x как локальную, эффективно используя Block». Это ли не объяснение того, почему в редактировании работает все, кроме метода 1? - person tomd; 25.12.2010
comment
Я не пытался намекнуть, что вы не знаете о области видимости (извините, если мой предыдущий комментарий звучит поучительно). Область видимости иногда бывает тонкой, как в вашем случае 1 в редактировании. Проблема не в x, поскольку он используется как шаблон в определении функции, а опять же в a и b. Слишком коряво писать это здесь полностью в комментарии без форматирования. Возможно, вы можете опубликовать свое редактирование как вопрос. Уверен, вы получите интересные ответы. - person Dr. belisarius; 25.12.2010