开发者

Fixing Combinatorica redefinition of Element

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 r开发者_如何学JAVAemove 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]}


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).


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.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜