开发者

Getting current context from within Package

I had something like the following in my notebook.

test1[g_] := (g == 5);
test2[g_] := (g == 6);
tests={"test1", "test2"}
ToExpression[#][5] & /@ tests

When I put this code in a package it doesn't work because test1 is now called MyPackage'Private'test1. How can I modify the last line to make this code run both inside package and inside notebook?

Update Here's why I was doing doing ToExpression as opposed to using Symbols. In retrospect, maybe it's easier to use Symbols instead

I had a function which I call like getGraphs["LeafFree","Planar",!"Tree",...] to get all graphs that are leaf free, planar and not trees. Some of those strings are classes in GraphData, while others were my own classes. For each of my own classes I had a function with identical name, like LeafFree that tested the property. In the notebook, using ToExpression code like above was the quickest way to implement this.

getGraphs[n_Integer, cl__] := getGraphs[{n, n}, cl];
getGraphs[{nmin_Integer, nmax_Integer}, cl__] := 
 Module[{maxgraphnum = 100},
  customClasses = {"isLeafFree", ! "isLeafFree"};
  classes = {cl}\[Backslash]customClasses;
  builtinClasses = 
   GraphData["Classes"] \[Tilde] (Not /@ GraphData["Classes"]);
  Assert[classes \[Subset] builtinClasses];
  isLeafFree[gname_] := 
   FreeQ[GraphData[gname, "DegreeSequence"], 0 | 开发者_StackOverflow中文版1];

  posClasses = Cases[classes\[Backslash]customClasses, _String];
  posGroup = 
   If[posClasses == {}, GraphData[nmin ;; nmax], 
    GraphData[posClasses, nmin ;; nmax]];
  negClasses = classes\[Backslash]posClasses;
  negGroups = GraphData[#[[1]], nmin ;; nmax] & /@ negClasses;

  result = Complement[posGroup, Sequence @@ negGroups];
  customTest[g_] := 
   And @@ (ToExpression[#][g] & /@ ({cl} \[Intersection] 
        customClasses));
  (*result=Take[result,Min[Length[result],100]];*)

  result = Select[result, customTest]
  ]


I agree with the comments above that you should perhaps have a compelling reason to do this, but here goes. Here is a code I use in such cases, which allows your symbol to be parsed in whatever context you like, at run-time:

SetAttributes[ParseTimeNameSpaceWrapper,HoldFirst];
Options[ParseTimeNameSpaceWrapper] = {
  LocalizingContext->"MyLocalizingContext`",
  DefaultImportedContexts:>{"Imported1`", "Imported2`"},
  ExtraImportedContexts:>   {}
};



ParseTimeNameSpaceWrapper[code_,opts:OptionsPattern[]]:=
Module[{result,
  context = OptionValue[LocalizingContext],
  defcontexts = OptionValue[DefaultImportedContexts],
  extraContexts = OptionValue[ExtraImportedContexts],
  allContexts},
  allContexts = {Sequence@@defcontexts,Sequence@@extraContexts};
  BeginPackage[context,If[allContexts==={},Sequence@@{},allContexts]];      
    result = code;  
  EndPackage[];
  result
]; 

You can use options to specify some contexts where these symbols exist, that you want to import during the parse stage. You can call this from any package or notebook, and the symbol will be parsed according to whatever context you specify.

HTH

Edit:

Responding to a comment (since it made the question more specific): There is no question that at run-time Context[] will display whatever is the current context from where the function was called (Global in that case). I meant something else: Context has a syntax Context[symbol], to give a context of any symbol if it is on the $ContextPath. For example, Context[getGraphs] returns Bulatov'showGraphs'. Therefore, if you need to determine the context of some exported function automatically, you call Context[function]. You can use this to construct full names of other (private) functions of that package. Here is a self - contained example:

In[1]:= 
BeginPackage["MyTest`"]; 
f[x_, y_, context_: Context[f]] :=
  Module[{f1str = "function1", f2str = "function2", f1, f2}, 
    {f1, f2} = ToExpression[context <> "Private`" <> #] & /@ {f1str, f2str};
    f1[x, y];
    f2[x, y];];

Begin["`Private`"];

function1[x_, y_] :=  Print["In function1: arguments are ", x, " , ", y];
function2[x_, y_] :=  Print["In function2: arguments are ", x, " , ", y];

End[]
EndPackage[];

Out[6]= "MyTest`Private`"

In[8]:= f[1, 2]

During evaluation of In[8]:= In function1: arguments are 1 , 2

During evaluation of In[8]:= In function2: arguments are 1 , 2

where x,y are just some sample arguments. Then, you never actually supply the last argument, but you can use the context variable inside your function, to construct long names for your other functions, as in the sample code above. Or you could just plain use Context[f] inside body of the function, and not add any arguments to it.


ToExpression uses the current binding of $Context when creating symbols, so you can force your expression to be interpreted within a particular context thus:

Block[{$Context="MyPackage`Private`"}, ToExpression[#][5]] & /@ tests

I'm not sure I understand the circumstances of the original question. You can get the current context using $Context or Context[]... but ToExpression will automatically use the current context without intervention.

If I run the exhibited code in a notebook, it works fine. If I run it like this:

Begin["MyPackage`Private`"]
test1[g_] := (g == 5);
test2[g_] := (g == 6);
tests = {"test1", "test2"}
ToExpression[#][5] & /@ tests
End[]

... it also works fine. I can get it to fail if I run it like this:

(* in the package file *)
Begin["MyPackage`Private`"]
test1[g_] := (g == 5);
test2[g_] := (g == 6);
End[]

(* in the notebook *)
tests = {"test1", "test2"}
ToExpression[#][5] & /@ tests

... which not only fails but also creates spurious symbols in the notebook's context. You can work around this problem using the Block recipe from above.

If you want to capture the context that was in effect at the moment the package code was loaded, you can do something like this:

(* in the package *)
Begin["MyPackage`Private`"]
test1[g_] := (g == 5);
test2[g_] := (g == 6);
tests = {"test1", "test2"};
With[{context = $Context},
  runTests[] := Block[{$Context = context}, ToExpression[#][5]] & /@ tests
]
End[]

(* in the notebook *)
MyPackage`Private`runTests[]

runTests uses With to inject the private package context into its definition.

As Janus' noted, it is better to use symbols than strings since they automatically manage the whole context issue -- but this assumes your actual use case will permit the use of symbols.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜