График с использованием «С» и «График» с использованием блока (Mathematica) - PullRequest
26 голосов
/ 04 июня 2011

Я хочу описать проблему, которая у меня возникла с Plot с использованием With для сохранения определенных параметров «локальными».Я не обязательно прошу исправить: у меня проблема в понимании.

Иногда я использую конструкцию, подобную следующей, чтобы получить график:

Метод 1

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

Мне нравится этот метод, и он достаточно понятен даже для не Mathematica пользователей точно, что происходит.

Когда уравнения должны бытьplotted становятся более сложными, мне нравится определять их вне графика (используя SetDelayed).Например:

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

Однако следующее не работает

Метод 2

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

У меня естьвсегда наивно думал, что так и должно быть.Тем не менее, основываясь на инструкции Help, что

Plot обрабатывает переменную x как локальную, эффективно используя Block

Я использовал различные обходные пути, в основном что-то вроде следующего

Метод 3

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

Этот кажется очень неловким, и обычно требует дальнейшего объяснения даже для Mathematica пользователей.

Вывод графиков

enter image description here

Однако недавно я случайно обнаружил, что замена Block на With в методе 2 работает точно так, как ожидалось.

Я могу, например, сделать что-то вроде следующего (что мне кажется очень универсальным подходом):

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

давая

enter image description here

Мои вопросы следующие.Чем объясняется разница в поведении с With в методах 1 и 2?Должен ли я ожидать, что метод 2 не сработает?Кроме того, чем объясняется разница в поведении с Block и With в методе 2?Должен ли я быть в состоянии предсказать, что Block будет работать?

Как ни странно, многие опытные люди предложили мне более опытные, чем я, но никто не предложил использовать Block.

Наконец, мне нужно оставить vmax и km локальными. (Они были определены алгебраически в другом месте)

Ответы [ 2 ]

62 голосов
/ 04 июня 2011

Ваш вопрос не столько о Plot, сколько о том, как работают контекстные конструкции. Основное замешательство здесь связано с различиями между лексическим и динамическим ограничением. И главный виновник этого определения:

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

Проблема в том, что он делает f неявно зависимым от глобальных символов (переменных) vmax и km. Я очень против такого рода конструкций, поскольку они приводят к бесконечной путанице. Теперь, что происходит, можно проиллюстрировать на следующем примере:

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

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

Чтобы понять, почему это происходит, нужно понять, что означает лексическая область видимости. Мы знаем, что With имеет атрибут HoldAll. Он работает так, что выглядит так: буквально внутри него, и подставляет переменные, найденные буквально в теле, в их значения из списка объявлений. Это происходит на этапе привязки переменных, и только тогда это позволяет телу оценить. Из этого ясно, что будет работать следующее:

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

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

Это сработало, потому что Evaluate переопределяет «часть» атрибута HoldAll в With, заставляя тело выполнять оценку перед чем-либо еще (привязка переменной и последующая оценка тела). Следовательно, было бы полностью эквивалентно использовать только With[{vmax = 1, km = 2}, (vmax x)/(km + x)] выше, как вы можете видеть с Trace. Следующая часть головоломки - почему

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

не работает. Это потому, что на этот раз мы не сначала оцениваем тело. Наличие Evaluate влияет только на f[x] внутри Plot, но не на оценку самого Plot внутри With. Это иллюстрируется

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

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

Более того, мы не хотим, чтобы Plot сначала выполнял оценку, поскольку тогда значения vmax и km не будут определены. Однако все, что видит With, это f[x], и поскольку параметры vmax и km не буквально присутствуют там (лексическая область видимости, помните), никакая замена не будет сделана. Если мы используем Block здесь, и все будет работать, потому что Block использует динамическую область видимости, что означает, что он переопределяет значения во времени (часть стека выполнения, если хотите), а не на месте. Следовательно, использование Block[{a =1, b =2}, ff[x]], где ff неявно зависит от a, а b (приблизительно) эквивалентно a=1;b=2;ff[x] (с той разницей, что a и b возобновляют свои глобальные значения после Block сфера оставлена). Итак,

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

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

Чтобы версия With работала, вам нужно ввести выражение для f[x] (r.h.s), например, так:

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

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

Обратите внимание, что это не сработает:

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

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

Но причина здесь довольно тонкая: хотя внешний With оценивает перед внутренним, он обнаруживает конфликты имен переменных и переименовывает их переменные. Правила гораздо более разрушительны, они не уважают внутренние ограничивающие конструкции.

РЕДАКТИРОВАТЬ

Если кто-то настаивает на вложенных With -s, вот как можно обмануть механизм разрешения конфликтов имен With и заставить его работать:

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 эффективно динамически генерируемым), он не производит никаких переименований, и затем он работает. Это общая хитрость, чтобы обмануть лексический механизм разрешения имен, когда вы не хотите переименовывать, хотя необходимость его использования обычно указывает на плохой дизайн.

КОНЕЦ РЕДАКТИРОВАНИЯ

Но я отвлекся. Подводя итог, заставить ваш второй метод работать довольно сложно и требует действительно странных конструкций, таких как

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 не нуждается в этом, он динамически заменяет значения в момент оценки на основе их измененных глобальных значений, как если бы вы делали присваивания, поэтому он работает.

Теперь настоящий виновник - ваше определение f.Вы могли бы избежать всех этих проблем, если бы вы определили свой f с явной передачей параметров:

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

Теперь это работает из коробки:

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

, потому чтопараметры явно присутствуют в сигнатуре вызова функции и поэтому видны для With.

Подводя итог: то, что вы наблюдали, является следствием взаимодействия лексической и динамической областей видимости.Лексические ограничивающие конструкции должны «видеть» свои переменные явно в коде на этапе привязки переменных (до оценки), иначе они не будут эффективными.Динамическая область видимости эффективно изменяет значения символов и в этом смысле менее требовательна (цена, которую вы платите, состоит в том, что код, использующий много динамической области видимости, сложнее понять, поскольку он смешивает состояние и поведение).Основной причиной проблем является определение функции, которая создает неявные зависимости от глобальных символов (которых нет в списке формальных параметров функции).Лучше всего избегать таких конструкций.Все еще можно заставить вещи работать, но это значительно сложнее (как было показано выше), и, по крайней мере, для рассматриваемого случая, без веской причины.

1 голос
/ 10 января 2018

Всего два комментария:

  • с использованием блока, вам не нужно использовать Evaluate.То есть Блок [{vmax = 10, km = 2}, участок [f [x], {x, 0, 100}] будет работать.

  • Другой способ сделать этоопределить правила замены: rule = {vmax -> 10, km -> 10};Участок [f [x] /.rule, {x, 0, 100}] Преимущество состоит в том, что вы можете повторно использовать правило в других выражениях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...