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