我的代码依赖于Element
的版本,其工作方式与MemberQ
类似,但当我加载Combinatorica
时,Element
会重新定义为Part
。解决这场冲突的最简单方法是什么?具体来说,从DownValues
中删除Combinatorica定义的语法是什么?以下是DownValues[Element]
{HoldPattern[
Combinatorica`Private`a_List \[Element] \
{Combinatorica`Private`index___}] :>
Combinatorica`Private`a[[Combinatorica`Private`index]],
HoldPattern[Private`x_ \[Element] Private`list_List] :>
MemberQ[Private`list, Private`x]}
答案 0 :(得分:4)
如果你的目标是阻止Combinatorica首先安装定义,你可以通过第一次加载包来实现这个结果:
Block[{Element}, Needs["Combinatorica`"]]
但是,这几乎肯定会使依赖于定义的任何Combinatorica功能失败(在您的特定应用程序中可能会或可能不会引起关注)。
答案 1 :(得分:3)
你可以做几件事。让我们介绍一个便利功能
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
编辑:
如果你需要自动使用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[]
您可以加载包并查看f1和f2的DownValues,例如:
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]]}
同样的方案也适用于不在同一个包中的功能。事实上,你可以分开
底部(代码处理包)本身就是一个包,将其导入到任何其他包中
你想要注入阻止你的函数定义的包,然后像上面那样调用postprocessDefs[mainContext]
之类的东西。你可以使在Block(elementDef
这里)内定义的函数成为elemExec
的通用版本的额外参数,这将使这种方法更加模块化和可重用。
如果您希望对要注入Block的函数更具选择性,也可以通过各种方式完成。事实上,整个Block-injection方案可以变得更加清洁,但是在实现每个功能时需要稍微小心,而上述方法是完全自动的。如果需要,我可以发布代码来说明这一点。
还有一件事:对于这种方法的侵入性较小,你要付出代价 - 动态范围(Block)通常比词法范围的构造更难控制。因此,您必须准确了解评估堆栈中要应用的部分。例如,我会毫不犹豫地将Block注入高阶函数的定义,该函数将一些函数作为参数,因为这些函数可能来自假定其他定义的代码(例如Combinatorica函数依赖于重载元素)。这不是一个大问题,只需要小心。
这个的底线似乎是:尽量避免重载内置插件。在这种情况下,你面对这个定义会发生冲突,但如果遇到这个问题的人是你的包裹的用户(几个月后可能是你自己),谁想要将你的包装与另一个包装结合起来,那就更糟了。碰巧与你的系统功能相同的系统功能。当然,它还取决于谁将是您的包的用户 - 只有您自己或潜在的其他人。但就设计而言,从长远来看,你可能最好从一开始就假设后一种情况。
答案 2 :(得分:1)
要删除Combinatorica
的定义,请使用Unset
或等效表单=.
。要取消设置的模式可以从问题中显示的Information
输出中获取:
Unprotect[Element];
Element[a_List, {index___}] =.
Protect[Element];
当然,担心Combinatorica
内部依赖于这种错误构思的重新定义,但你有理由相信这不是重新定义的Information
输出Element
{1}}说:
使用该功能 Combinatorica中的元素现在 过时,虽然函数调用 元素[a,p]仍然给出了pth 嵌套列表a的元素,其中p是a 指数清单。
HTH
答案 3 :(得分:1)
我提出了一种完全不同的方法,而不是从DownValues中删除Element。只需使用Element功能的全名即可。
所以,如果原件是
System`Element[]
现在默认为
Combinatorica`Element[]
因为加载了Combinatorica包。
只需明确使用
System`Element[]
无论你需要它。当然使用Context函数检查System是否是正确的Context:
Context[Element]
这种方法确保了几件事:
唯一的缺点是每次都必须明确地写它。