Ответ 1
Вот попытка. Идея состоит в том, чтобы идентифицировать символы с DownValues
или некоторыми другими ...Values
внутри вашего управляемого кода и автоматически переименовывать их с помощью уникальных переменных/символов вместо них. Идею здесь можно выполнить довольно изящно с помощью функциональных возможностей клонирования символов, которые я время от времени нахожу полезными. Функция clone
ниже будет клонировать данный символ, создавая символ с теми же глобальными определениями:
Clear[GlobalProperties];
GlobalProperties[] :=
{OwnValues, DownValues, SubValues, UpValues, NValues, FormatValues,
Options, DefaultValues, Attributes};
Clear[unique];
unique[sym_] :=
ToExpression[
ToString[Unique[sym]] <>
StringReplace[StringJoin[ToString /@ Date[]], "." :> ""]];
Attributes[clone] = {HoldAll};
clone[s_Symbol, new_Symbol: Null] :=
With[{clone = If[new === Null, unique[Unevaluated[s]], ClearAll[new]; new],
sopts = Options[Unevaluated[s]]},
With[{setProp = (#[clone] = (#[s] /. HoldPattern[s] :> clone)) &},
Map[setProp, DeleteCases[GlobalProperties[], Options]];
If[sopts =!= {}, Options[clone] = (sopts /. HoldPattern[s] :> clone)];
HoldPattern[s] :> clone]]
Существует несколько альтернатив реализации самой функции. Один из них - ввести функцию с другим именем, используя те же аргументы, что и Manipulate
, скажем myManipulate
. Я буду использовать еще один: мягко перегрузите Manipulate
через UpValues
какой-либо пользовательской оболочки, которую я представлю. Я назову его CloneSymbols
. Вот код:
ClearAll[CloneSymbols];
CloneSymbols /:
Manipulate[args___,CloneSymbols[sd:(SaveDefinitions->True)],after:OptionsPattern[]]:=
Unevaluated[Manipulate[args, sd, after]] /.
Cases[
Hold[args],
s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :>
clone[s],
Infinity, Heads -> True];
Вот пример использования:
f[x_] := Sin[x];
g[x_] := x^2;
Обратите внимание, что для использования новой функциональности нужно обернуть параметр SaveDefinitions->True
в обертку CloneSymbols
:
Manipulate[Plot[ f[n g[x]], {x, -3, 3}], {n, 1, 4},
CloneSymbols[SaveDefinitions -> True]]
Это не повлияет на определения исходных символов в коде внутри Manipulate
, так как это были их клоны, определения которых были сохранены и использованы при инициализации. Мы можем посмотреть FullForm
для этого Manipulate
, чтобы подтвердить, что:
Manipulate[Plot[f$37782011751740542578125[Times[n,g$37792011751740542587890[x]]],
List[x,-3,3]],List[List[n,1.9849999999999999`],1,4],RuleDelayed[Initialization,
List[SetDelayed[f$37782011751740542578125[Pattern[x,Blank[]]],Sin[x]],
SetDelayed[g$37792011751740542587890[Pattern[x,Blank[]]],Power[x,2]]]]]
В частности, вы можете изменить определения функций, чтобы сказать
f[x_]:=Cos[x];
g[x_]:=x;
Затем переместите ползунок Manipulate
выше, а затем проверьте определения функций
?f
Global`f
f[x_]:=Cos[x]
?g
Global`g
g[x_]:=x
Этот Manipulate
достаточно независим от всего и может быть скопирован и вставлен безопасно. Здесь происходит следующее: сначала мы найдем все символы с нетривиальными DownValues
, SubValues
или UpValues
(возможно, можно добавить и OwnValues
) и использовать Cases
и clone
для создания их клоны на лету. Затем мы лексически заменяем все клонированные символы на свои клоны внутри Manipulate
, а затем пусть Manipulate
сохраняем определения для клонов. Таким образом, мы делаем "моментальный снимок" задействованных функций, но никак не влияем на исходные функции.
Уникальность клонов (символов) была решена с помощью функции unique
. Однако обратите внимание, что хотя полученные таким образом Manipulate
-s не угрожают определениям исходных функций, они, как правило, будут зависеть от них, поэтому их нельзя считать абсолютно независимыми от чего-либо. Нужно было бы спуститься по дереву зависимостей и клонировать все символы там, а затем восстановить их взаимозависимости, чтобы создать полностью автономный "снимок" в Manipulate. Это выполнимо, но сложнее.
ИЗМЕНИТЬ
По запросу @Sjoerd я добавляю код для случая, когда мы хотим, чтобы наши Manipulate
-s обновлялись до изменений функции, но не хотят, чтобы они активно вмешивались и изменяли любые глобальные определения. Я предлагаю вариант метода "указатель": мы снова будем заменять имена функций новыми символами, но вместо того, чтобы клонировать эти новые символы после наших функций, мы будем использовать параметр Manipulate
Initialization
, чтобы просто сделать эти символы "указатели" на наши функции, например, как Initialization:>{new1:=f,new2:=g}
. Ясно, что переоценка такого кода инициализации не может нанести вреда определениям f
или g
, и в то же время наши Manipulate
-s будут реагировать на изменения этих определений.
Первая мысль состоит в том, что мы могли просто просто заменить имена функций новыми символами, и пусть Manipulate
инициализация автоматически сделает все остальное. К сожалению, в этом процессе он просматривает дерево зависимостей, и поэтому будут включены определения для наших функций - чего мы пытаемся избежать. Итак, вместо этого мы явно построим опцию Initialize
. Вот код:
ClearAll[SavePointers];
SavePointers /:
Manipulate[args___,SavePointers[sd :(SaveDefinitions->True)],
after:OptionsPattern[]] :=
Module[{init},
With[{ptrrules =
Cases[Hold[args],
s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :>
With[{pointer = unique[Unevaluated[s]]},
pointer := s;
HoldPattern[s] :> pointer],
Infinity, Heads -> True]},
Hold[ptrrules] /.
(Verbatim[HoldPattern][lhs_] :> rhs_ ) :> (rhs := lhs) /.
Hold[defs_] :>
ReleaseHold[
Hold[Manipulate[args, Initialization :> init, after]] /.
ptrrules /. init :> defs]]]
С теми же определениями, что и раньше:
ClearAll[f, g];
f[x_] := Sin[x];
g[x_] := x^2;
Вот FullForm
созданного Manipulate
:
In[454]:=
FullForm[Manipulate[Plot[f[n g[x]],{x,-3,3}],{n,1,4},
SavePointers[SaveDefinitions->True]]]
Out[454]//FullForm=
Manipulate[Plot[f$3653201175165770507872[Times[n,g$3654201175165770608016[x]]],
List[x,-3,3]],List[n,1,4],RuleDelayed[Initialization,
List[SetDelayed[f$3653201175165770507872,f],SetDelayed[g$3654201175165770608016,g]]]]
Сгенерированные символы служат "указателями" на наши функции. Manipulate
-s, построенные с помощью этого подхода, будут реагировать на обновления в наших функциях и в то же время безвредны для определения основных функций. Цена за оплату заключается в том, что они не являются автономными и не будут отображаться правильно, если основные функции undefined. Таким образом, можно использовать оболочку CloneSymbols
или SavePointers
, в зависимости от того, что необходимо.