Вы можете сделать несколько вещей. Введем удобную функцию
ClearAll[redef];
SetAttributes[redef, HoldRest];
redef[f_, code_] := (Unprotect[f]; code; Protect[f])
Если вы уверены в порядке определений, вы можете сделать что-то вроде
redef[Element, DownValues[Element] = Rest[DownValues[Element]]]
Если вы хотите удалить определения, основанные на контексте, вы можете сделать что-то вроде этого:
redef[Element, DownValues[Element] =
DeleteCases[DownValues[Element],
rule_ /; Cases[rule, x_Symbol /; (StringSplit[Context[x], "`"][[1]] ===
"Combinatorica"), Infinity, Heads -> True] =!= {}]]
Вы также можете использовать более мягкий способ - переупорядочивать определения, а не удалять:
redef[Element, DownValues[Element] = RotateRight[DownValues[Element]]]
Есть много других способов решения этой проблемы. Другой (который я уже рекомендовал) - использовать UpValues, если это подходит. Последнее, что я хотел бы упомянуть здесь, - это создать своего рода динамическую конструкцию области видимости, основанную на Block, и обернуть ее вокруг вашего кода. Я лично считаю, что это самый безопасный вариант, если вы хотите строго применить ваше определение (потому что его не волнует порядок, в котором могли быть созданы различные определения - он удаляет все из них и добавляет только ваше). Также безопаснее, когда за пределами тех мест, где вы хотите, чтобы ваши определения применялись (под «местами» я подразумеваю части стека оценки), другие определения все равно будут применяться, так что это кажется наименее навязчивым способом. Вот как это может выглядеть:
elementDef[] := Element[x_, list_List] := MemberQ[list, x];
ClearAll[elemExec];
SetAttributes[elemExec, HoldAll];
elemExec[code_] := Block[{Element}, elementDef[]; code];
Пример использования:
In[10]:= elemExec[Element[1,{1,2,3}]]
Out[10]= True
Edit:
Если вам нужно автоматизировать использование Block, вот пример пакета, показывающий один из способов, как это можно сделать:
BeginPackage["Test`"]
var;
f1;
f2;
Begin["`Private`"];
(* Implementations of your functions *)
var = 1;
f1[x_, y_List] := If[Element[x, y], x^2];
f2[x_, y_List] := If[Element[x, y], x^3];
elementDef[] := Element[x_, list_List] := MemberQ[list, x];
(* The following part of the package is defined at the start and you don't
touch it any more, when adding new functions to the package *)
mainContext = StringReplace[Context[], x__ ~~ "Private`" :> x];
SetAttributes[elemExec, HoldAll];
elemExec[code_] := Block[{Element}, elementDef[]; code];
postprocessDefs[context_String] :=
Map[
ToExpression[#, StandardForm,
Function[sym,DownValues[sym] =
DownValues[sym] /.
Verbatim[RuleDelayed][lhs_,rhs_] :> (lhs :> elemExec[rhs])]] &,
Select[Names[context <> "*"], ToExpression[#, StandardForm, DownValues] =!= {} &]];
postprocessDefs[mainContext];
End[]
EndPackage[]
Вы можете загрузить пакет и посмотреть значения DownValues для f1 и f2, например:
In[17]:= DownValues[f1]
Out[17]= {HoldPattern[f1[Test`Private`x_,Test`Private`y_List]]:>
Test`Private`elemExec[If[Test`Private`x\[Element]Test`Private`y,Test`Private`x^2]]}
Та же схема будет работать и для функций, не входящих в тот же пакет. На самом деле, вы могли бы отделить
нижняя часть (пакет обработки кода) должна быть пакетом сама по себе, импортировать ее в любой другой
пакет, в который вы хотите добавить Block в определения ваших функций, а затем просто вызвать что-то вроде postprocessDefs[mainContext]
, как указано выше. Вы можете сделать функцию, которая делает определения внутри блока (elementDef
здесь), дополнительным параметром для обобщенной версии elemExec
, что сделает этот подход более модульным и многократно используемым.
Если вы хотите более избирательно относиться к функциям, в которые вы хотите ввести блокировку, это также можно сделать различными способами. Фактически, тогда вся схема внедрения блоков может быть более чистой, но при реализации каждой функции потребуется немного больше внимания, в то время как описанный выше подход является полностью автоматическим. При необходимости я могу опубликовать код, который проиллюстрирует это.
Еще одна вещь: за менее навязчивую природу этого метода вы платите цену - динамический контекст (блок) обычно сложнее контролировать, чем конструкции с лексической областью. Таким образом, вы должны точно знать, какие части стека оценки вы хотите применить. Например, я бы не хотел вставлять Блок в определение функции более высокого порядка, которая принимает некоторые функции в качестве параметров, поскольку эти функции могут происходить из кода, который принимает другие определения (как, например, функции Combinatorica, основанные на перегруженном элементе). Это не большая проблема, просто требует ухода.
Суть в следующем: кажется, старайтесь избегать перегрузки встроенных модулей, если это вообще возможно. В этом случае вы столкнулись с этим определением столкновения самих себя, но было бы еще хуже, если бы тот, кто сталкивается с этой проблемой, был пользователем вашего пакета (может быть, вы сами через несколько месяцев), который хочет объединить ваш пакет с другим (который происходит перегрузка тех же системных функций, что и у вас). Конечно, это также зависит от того, кто будет пользователями вашего пакета - только вы или потенциально другие. Но с точки зрения дизайна и в долгосрочной перспективе вам, возможно, будет лучше принять последний сценарий с самого начала.