修复 Combinatorica 元素的重新定义

发布于 2024-10-12 08:39:52 字数 551 浏览 2 评论 0原文

我的代码依赖于 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]}

My code relies on version of Element which works like MemberQ, but when I load Combinatorica, Element gets redefined to work like Part. What is the easiest way to fix this conflict? Specifically, what is the syntax to remove Combinatorica's definition from DownValues? Here's what I get for 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]}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

美羊羊 2024-10-19 08:39:52

如果您的目标是首先阻止 Combinatorica 安装定义,则可以通过首次加载包来实现此结果:

Block[{Element}, Needs["Combinatorica`"]]

但是,这几乎肯定会使任何依赖于定义的 Combinatorica 功能失败(这可能会导致或者在您的特定应用程序中可能不关心)。

If your goal is to prevent Combinatorica from installing the definition in the first place, you can achieve this result by loading the package for the first time thus:

Block[{Element}, Needs["Combinatorica`"]]

However, this will almost certainly make any Combinatorica features that depend upon the definition fail (which may or may not be of concern in your particular application).

清引 2024-10-19 08:39:52

您可以做几件事。让我们介绍一个方便的功能

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 的 DownValues和 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] 的内容,如上所述。您可以将在 Block 内进行定义的函数(此处为 elementDef)作为 elemExec 通用版本的额外参数,这将使该方法更加模块化和可重用。

如果您想对要注入 Block 的函数更有选择性,也可以通过多种方式来完成。事实上,整个Block-injection方案可以变得更干净,但是在实现每个功能时需要稍微小心一些,而上述方法是完全自动的。如果需要的话,我可以发布代码来说明这一点。

另一件事:为了这种方法的侵入性较小,您付出了代价 - 动态作用域(块)通常比词法作用域构造更难控制。因此,您必须确切地了解您希望应用该计算堆栈的部分。例如,我会犹豫是否将 Block 注入到高阶函数的定义中,该函数将某些函数作为参数,因为这些函数可能来自采用其他定义的代码(例如依赖于重载 Element 的 Combinatorica` 函数)。这不是一个大问题,只是需要小心。

底线似乎是:尽可能避免内置函数过载。在这种情况下,您自己会遇到这个定义冲突,但如果面临此问题的人是您的软件包的用户(可能是几个月后的您自己),他想要将您的软件包与另一个软件包(这碰巧重载了与您相同的系统功能)。当然,这还取决于谁将是您的软件包的用户 - 仅您自己或潜在的其他人。但就设计而言,从长远来看,从一开始就假设后一种情况可能会更好。

You can do several things. Let us introduce a convenience function

ClearAll[redef];
SetAttributes[redef, HoldRest];
redef[f_, code_] := (Unprotect[f]; code; Protect[f])

If you are sure about the order of definitions, you can do something like

redef[Element, DownValues[Element] = Rest[DownValues[Element]]]

If you want to delete definitions based on the context, you can do something like this:

redef[Element, DownValues[Element] = 
    DeleteCases[DownValues[Element],
          rule_ /; Cases[rule, x_Symbol /; (StringSplit[Context[x], "`"][[1]] === 
                 "Combinatorica"), Infinity, Heads -> True] =!= {}]]

You can also use a softer way - reorder definitions rather than delete:

redef[Element, DownValues[Element] = RotateRight[DownValues[Element]]]

There are many other ways of dealing with this problem. Another one (which I already recommended) is to use UpValues, if this is suitable. The last one I want to mention here is to make a kind of custom dynamic scoping construct based on Block, and wrap it around your code. I personally find it the safest variant, in case if you want strictly your definition to apply (because it does not care about the order in which various definitions could have been created - it removes all of them and adds just yours). It is also safer in that outside those places where you want your definitions to apply (by "places" I mean parts of the evaluation stack), other definitions will still apply, so this seems to be the least intrusive way. Here is how it may look:

elementDef[] := Element[x_, list_List] := MemberQ[list, x];

ClearAll[elemExec];
SetAttributes[elemExec, HoldAll];
elemExec[code_] :=  Block[{Element},   elementDef[];   code];

Example of use:

In[10]:= elemExec[Element[1,{1,2,3}]]

Out[10]= True

Edit:

If you need to automate the use of Block, here is an example package to show one way how this can be done:

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[]

You can load the package and look at the DownValues for f1 and f2, for example:

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]]}

The same scheme will also work for functions not in the same package. In fact, you could separate
the bottom part (code-processing package) to be a package on its own, import it into any other
package where you want to inject Block into your functions' definitions, and then just call something like postprocessDefs[mainContext], as above. You could make the function which makes definitions inside Block (elementDef here) to be an extra parameter to a generalized version of elemExec, which would make this approach more modular and reusable.

If you want to be more selective about the functions where you want to inject Block, this can also be done in various ways. In fact, the whole Block-injection scheme can be made cleaner then, but it will require slightly more care when implementing each function, while the above approach is completely automatic. I can post the code which will illustrate this, if needed.

One more thing: for the less intrusive nature of this method you pay a price - dynamic scope (Block) is usually harder to control than lexically-scoped constructs. So, you must know exactly the parts of evaluation stack where you want that to apply. For example, I would hesitate to inject Block into a definition of a higher order function, which takes some functions as parameters, since those functions may come from code that assumes other definitions (like for example Combinatorica` functions relying on overloaded Element). This is not a big problem, just requires care.

The bottom line of this seems to be: try to avoid overloading built-ins if at all possible. In this case you faced this definitions clash yourself, but it would be even worse if the one who faces this problem is a user of your package (may be yourself a few months later), who wants to combine your package with another one (which happens to overload same system functions as yours). Of course, it also depends on who will be the users of your package - only yourself or potentially others as well. But in terms of design, and in the long term, you may be better off assuming the latter scenario from the start.

≈。彩虹 2024-10-19 08:39:52

要删除 Combinatorica 的定义,请使用 Unset< /code>或等效形式 =.。您可以从问题中显示的 Information 输出中获取要取消设置的模式:

Unprotect[Element];
Element[a_List, {index___}] =.
Protect[Element];

当然,担心的是 Combinatorica 在内部依赖于这种考虑不周的重新定义,但您有理由相信情况并非如此,因为重新定义的 ElementInformation 输出显示:

函数的使用
Combinatorica 中的元素现在是
已过时,但函数调用
Element[a, p] 仍然给出第 p 个
嵌套列表 a 的元素,其中 p 是 a
索引列表。

华泰

To remove Combinatorica's definition, use Unset or the equivalent form =.. The pattern to unset you can grab from the Information output you show in the question:

Unprotect[Element];
Element[a_List, {index___}] =.
Protect[Element];

The worry would be, of course, that Combinatorica depends internally on this ill-conceived redefinition, but you have reason to believe this to not be the case as the Information output from the redefined Element says:

The use of the function
Element in Combinatorica is now
obsolete, though the function call
Element[a, p] still gives the pth
element of nested list a, where p is a
list of indices.

HTH

相权↑美人 2024-10-19 08:39:52

我提出了一种与从 DownValues 中删除元素完全不同的方法。只需使用 Element 函数的全名即可。

因此,如果原来是

System`Element[]

默认值,那么现在是

Combinatorica`Element[]

因为加载了 Combinatorica 包。

明确使用即可

System`Element[]

只要在需要的地方 。当然,使用 Context 函数检查系统是否是正确的上下文:

Context[Element]

这种方法可以确保以下几点:

  1. 即使 Combinatorica 包将来更新,Combinatorica 包仍将在您的笔记本中工作
  2. 您不必重新定义 Element 函数,因为有些人建议
  3. 您可以在需要时使用 Combinatorica`Element 函数,

唯一的缺点是每次都必须显式编写它。

I propose an entirely different approach than removing Element from DownValues. Simply use the full name of the Element function.

So, if the original is

System`Element[]

the default is now

Combinatorica`Element[]

because of loading the Combinatorica Package.

Just explicitly use

System`Element[]

wherever you need it. Of course check that System is the correct Context using the Context function:

Context[Element]

This approach ensures several things:

  1. The Combinatorica Package will still work in your notebook, even if the Combinatorica Package is updated in the future
  2. You wont have to redefine the Element function, as some have suggested
  3. You can use the Combinatorica`Element function when needed

The only downside is having to explicitly write it every time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文