Использование графика с использованием графика с использованием блока (Mathematica)

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

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

Метод 1

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

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

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

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

Однако, не работает

Метод 2

plot2 = With[{vmax = 10, km = 10}, 
  Plot[[email protected][x], {x, 0, 100}, AxesOrigin -> {0, 0}]]

Я всегда наивно думал, что нужно. Однако на основе справки, в которой

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

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

Метод 3

plot3 = Plot[With[{vmax = 10, km = 10}, [email protected][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[[email protected][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 local. (Они были определены алгебраически в другом месте)

Ответы

Ответ 1

Ваш вопрос не столько о 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[[email protected][x], {x, 0, 100}, AxesOrigin -> {0, 0}]]

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

In[59]:= With[{vmax = 10, km = 10}, q[[email protected][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[[email protected][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), он не производит никаких переименований, а затем он работает. Это общий трюк, чтобы обмануть лексический механизм разрешения имен имен, если вы не хотите переименовывать, хотя необходимость его использования обычно указывает на плохой дизайн.

END EDIT

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

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

или

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

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

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

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

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

With[{vmax = 10, km = 10}, 
   Plot[[email protected][x, vmax, km], {x, 0, 100}, AxesOrigin -> {0, 0}]]

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

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

Ответ 2

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

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

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