【问题标题】:Getting current context from within Package从包中获取当前上下文
【发布时间】:2011-01-28 04:34:02
【问题描述】:

我的笔记本中有类似以下内容。

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

当我将此代码放入包中时,它不起作用,因为 test1 现在称为 MyPackage'Private'test1。如何修改最后一行以使此代码在包内和笔记本内运行?

更新 这就是为什么我使用 ToExpression 而不是使用 Symbols 的原因。回想起来,也许改用符号更容易

我有一个函数,我称之为 getGraphs["LeafFree","Planar",!"Tree",...] 来获取所有无叶、平面而不是树的图形。其中一些字符串是GraphData 中的类,而另一些是我自己的类。对于我自己的每个类,我都有一个名称相同的函数,例如测试属性的LeafFree。在 notebook 中,使用上述ToExpression 代码是实现这一点的最快方法。

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

【问题讨论】:

  • 你试过StringJoin[Context[], #] & /@ {"test1", "test2"}
  • 我认为ToExpression 是不好的风格鸭子。有没有理由使用字符串而不是符号?我会考虑 testsAsSymbols = {test1, test2}Through[testsAsSymbols[5]] 更好 - 它在这两种情况下都可以工作?
  • @Janus:你应该这样回答......
  • 哈!今天每个人都很害羞:)
  • belisarius:是的,不起作用,请参阅下面的我的 cmets。 Janus:我使用 ToExpression 是因为我正在做类似MyPackage'doChecks[{"test1","test2"}] 之类的事情,其中​​test1 是在函数doChecks 中定义的函数名称

标签: wolfram-mathematica


【解决方案1】:

我同意上述 cmets 的观点,即您可能应该有一个令人信服的理由这样做,但这里就是这样。这是我在这种情况下使用的代码,它允许在运行时在任何你喜欢的上下文中解析你的符号:

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

您可以使用选项来指定存在这些符号的一些上下文,您希望在解析阶段导入这些上下文。您可以从任何包或笔记本中调用它,符号将根据您指定的任何上下文进行解析。

HTH

编辑:

回复评论(因为它使问题更具体):毫无疑问,在运行时Context[] 将显示调用函数的当前上下文(在这种情况下为全局)。我的意思是别的:Context 有一个语法Context[symbol],如果它在$ContextPath 上,它可以给出任何符号的上下文。例如,Context[getGraphs] 返回Bulatov'showGraphs'。因此,如果您需要自动确定某些导出函数的上下文,请调用Context[function]。您可以使用它来构造该包的其他(私有)函数的全名。这是一个独立的例子:

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

x,y 只是一些示例参数。然后,您实际上永远不会提供最后一个参数,但您可以在函数中使用 context 变量来为其他函数构造长名称,如上面的示例代码所示。或者你可以在函数体中直接使用 Context[f] ,而不给它添加任何参数。

【讨论】:

  • 问题是我看不到获得正确上下文的方法。我有函数f,g,hf 被放入 Needs 上的 Global 上下文中,而 gh 保留在 MyPackage'Private 中。理想情况下,f 会以编程方式获取包的名称,但在执行f 期间它不在Context[]
  • @Yaroslav: 将函数导入 Global` 上下文不会使其属于 Global. Its context is still the same as before. It is just that for symbols from contexts on the $ContextPath, we can use short names. If I understood correctly and f` 与 gh 属于同一个包(只是不是 Private子包),那么你可以只做 Context[f] "Privateand h` are,然后构造它们的长名称,并将它们提供给 ToExpression(当然,Context[symbol] 将可靠地工作只有当没有阴影时,我认为这是真的)。​​
  • 如果我将Print[Context[]] 放在 f[] 中,即使 f 在包中定义,我也会打印出Global,这是一个独立的示例 -- yaroslavvb.com/upload/save/shifrin.zip
  • @Yaroslav 我更新了我的帖子,希望这能澄清我的意思。另外,我很抱歉现在离开,SPb 凌晨 4 点 50 分。目前。
【解决方案2】:

ToExpression 在创建符号时使用$Context 的当前绑定,因此您可以强制在特定上下文中解释您的表达式:

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

我不确定我是否了解原始问题的情况。您可以使用$ContextContext[]... 获取当前上下文,但ToExpression 将自动使用当前上下文而无需干预。

如果我在笔记本中运行展示的代码,它可以正常工作。如果我这样运行:

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

...它也可以正常工作。如果我这样运行它,我可能会失败:

(* 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

... 这不仅会失败,还会在笔记本的上下文中创建虚假符号。您可以使用上面的 Block 配方解决此问题。

如果您想捕获在加载包代码时生效的上下文,您可以执行以下操作:

(* 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 使用With 将私有包上下文注入到其定义中。

正如 Janus 所说,使用符号比使用字符串更好,因为它们会自动管理整个上下文问题——但这假设您的实际用例允许使用符号。

【讨论】:

  • Context[] 不起作用的原因是因为封闭函数被放入了Needs 上的 Global` 上下文中,所以 Context[] 在执行点是全局的,同时 test1,test2 是“内部”函数,因此它们保留在 MyPackagePrivate 上下文中
  • 答案顶部带有Block 的方法简单而优雅,但如果当前某些上下文中确实存在具有相同短(字符串)名称的符号,则会(默默地)失败在 $ContextPath 上(不一定意味着阴影)。一个简单的例子是:f[x_] := x^2; Block[{$Context = "Test'Private'"}, ToExpression["f"][x]]。这就是我在回答中使用BeginPackage - EndPackage 的原因之一——他们会解决这个问题。或者,您可以明确使用Block[{$Context = "Test'Private'", $ContextPath = {"Test'", "System'"}},...]
猜你喜欢
  • 1970-01-01
  • 2022-11-13
  • 1970-01-01
  • 2012-06-22
  • 2020-04-22
  • 2021-02-04
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
相关资源
最近更新 更多