Вот попытка. Идея состоит в том, чтобы идентифицировать символы с 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
-ы, полученные таким способом, не угрожают исходным определениям функций, они, как правило, все еще зависят от них, поэтому нельзя считать их полностью независимыми от чего-либо. Нужно пройтись по дереву зависимостей и клонировать все символы там, а затем восстановить их взаимозависимости, чтобы создать полностью автономный «снимок» в Manipulate. Это выполнимо, но сложнее.
EDIT
По запросу @Sjoerd я добавляю код для случая, когда мы хотим, чтобы наши Manipulate
обновляли изменения функции, но не хотим, чтобы они активно вмешивались и изменяли какие-либо глобальные определения. Я предлагаю вариант техники «указателя»: мы снова заменим имена функций новыми символами, но вместо того, чтобы клонировать эти новые символы после наших функций, мы будем использовать опцию Manipulate
Initialization
, чтобы просто сделать эти символы "указатели" на наши функции, например, Initialization:>{new1:=f,new2:=g}
. Ясно, что повторная оценка такого кода инициализации не может повредить определениям f
или g
, и в то же время наши Manipulate
-ы станут реагировать на изменения в этих определениях.
Первая мысль заключается в том, что мы могли бы просто заменить имена функций новыми символами и позволить инициализации 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
, созданные с использованием этого подхода, будут реагировать на обновления наших функций и в то же время безвредны для определений основных функций.Платой является то, что они не являются автономными и не будут отображаться правильно, если основные функции не определены.Таким образом, можно использовать либо CloneSymbols
оболочку, либо SavePointers
, в зависимости от того, что нужно.