将符号暴露给$ContextPath

24

有各种有用的 Internal` 上下文函数,例如InheritedBlockBagStuffBag等,以及许多有用的 Developer` 函数。

我希望暴露这些符号的一部分,以便可以明确地使用它们,而不需要上下文名称,但我不想通过向 $ContextPath 添加 Internal`Developer` 来公开所有内容。

我可以使用代理符号,例如 Bag = Internal`Bag,但这既不干净也不完全正确,因为它是一个引用,例如属性不会被继承。

是否有一种方法可以有选择地暴露我想要的符号,而不必采用上述方法?


1
@Simon,是的,我可以这样做,但通常情况下甚至都不需要,因为头部先进行评估。这本质上并不是一个问题,但不知怎么的,它似乎不正确。 - Mr.Wizard
4
出人意料的是,即使没有使用Unprotect或类似的工具,也可以使用Context[symbol] = "context`"重新定义SystemInternal符号的上下文。然而,这将从其原始上下文中移除该符号,这是不可接受的。有没有办法为一个符号分配两个不同的上下文? - Mr.Wizard
5
只是记录历史 http://i.stack.imgur.com/3ODWy.png - Dr. belisarius
1
@belisarius 关于你的问题,这很可能是处理它的最佳或唯一方法。然而,我知道这是一个指针,而不是符号本身,出于某种原因,这让我感到困扰。 - Mr.Wizard
@belisarius 我不是在谈论符号的克隆,这是回答问题的一种可能方法,但也许不是最好的方法。我指的是直接导入给定的特定符号,而不导入该符号的完整上下文 - 这种功能存在于某些语言中,例如Python。执行context`symbol实际上是一个很差的解决方案,因为它对于诸如符号移动到另一个上下文或上下文重命名等更改是脆弱的,并且可能导致回归错误 - 它是一种硬编码,有点破坏了模块/包的目的。 - Leonid Shifrin
显示剩余8条评论
2个回答

12

我认为这是一个非常深入和有价值的问题,根据投票数来看,我并不是唯一这样想的。如果我们在语言级别上完全支持了这个特性,那么这将给我们一种全新的方式来操作作用域和封装,通常会允许更清晰的代码和更好的信息隐藏。这类似于Python中的from module-name import name1,name2,...

也许和许多人一样,我尝试过多种方法,但它们都似乎脆弱且不完整。最糟糕的情况是对于包,我没有好的解决方案。对于在前端进行交互式工作,这里有一个可能可以接受的解决方案。首先定义一个通用的宏来进行文字替换:

ClearAll[withImported];
SetAttributes[withImported, HoldAll];
withImported[importingRules : {(_Symbol ->  _String) ..}, code_] :=
  With @@ Join[
     Hold[importingRules] /.
      (name_Symbol -> context_String) :>
          With[{eval =
               Block[{$ContextPath = Append[$ContextPath, context]},
                 ToExpression[context <> ToString[Unevaluated@name]]
               ]},
             Set[name, eval] /; True],
     Hold[code]];

withImported[importingRules : {({__Symbol} -> _String) ..}, code_] :=  
    Reverse[Hold @@ Flatten[Unevaluated[importingRules] /.
        ({syms__Symbol} -> s_String) :> Thread[s :> {syms}]], {2}] /. 
        RuleDelayed -> Rule /.
        Hold[expandedRules : ((_Symbol ->  _String) ..)] :> 
             withImported[{expandedRules}, code];

接下来,创建一个函数,其中包括你喜欢的快捷方式,例如:

shortcutF = 
   Function[code,
     withImported[
       {
         {PackedArrayQ, ToPackedArray, FromPackedArray} -> "Developer`",
         {InheritedBlock, WithLocalSettings} -> "Internal`"
       },
       code
     ],
     HoldAll];

现在您可以将代码包裹在shortcutF中并开始使用简称。目前为止,这也适用于包,但是您将不得不将所有代码(或至少包含快捷方式的那些部分)都包装在shortcutF中,这不是很方便。为了进一步方便,您可以将上述函数分配给$Pre

$Pre = shortcutF;

这里是一些使用示例:

In[31]:= 
WithLocalSettings[Null,Abort[],Print["Cleanup"]]

During evaluation of In[31]:= Cleanup
Out[31]= $Aborted[]

In[32]:= PackedArrayQ[Range[10]]
Out[32]= True

In[33]:= PackedArrayQ@FromPackedArray[Range[10]]
Out[33]= False

由于With在内部使用,因此在执行代码之前,您的快捷符号将被完全限定的符号名称替换。

这是我能做到的最好程度,但这个特性似乎特别需要语言本身的支持。


谢谢Leonid。请提醒我:您是否找到了一个在解析之前起作用的内核钩子,类似于FrontEnd的CellEvaluationFunction?将包的主体封装在字符串中是否可行,例如以parsefunction = ...body = "..."开头,然后使用ToExpression@parsefunction@body - Mr.Wizard
@Mr.Wizard,很遗憾我不知道这种钩子。至于在这种情况下使用字符串的实用性,这可能取决于具体情况,但我认为这不是一个普遍好的解决方案。我认为我们真正需要的是对您请求的特定功能的语言支持,或者一个可编程的读取器/解析器(这将更好)。 - Leonid Shifrin

5

这是对Leonid答案的改进,适用于更早的阶段:

InternalSymbols={"Bag","BagLength","BagPart","StuffBag"}
$PreRead=#/.(InternalSymbols/.{s_String:>s->"Internal`"<>s})&

在笔记本上输入此内容后,键入
?Bag

提供给我

Internal`Bag
Attributes[Internal`Bag]={Protected}

当...期间
?AbsSquare

提供

Information::notfound: Symbol AbsSquare not found.

但是
?Internal`AbsSquare

提供

Internal`AbsSquare
Attributes[Internal`AbsSquare]={Listable,NumericFunction,Protected}

然而,它似乎只在笔记本界面中起作用,在使用命令行上的数学时不起作用。

虽然在我提问之前我已经很清楚这种方法,但这是一个有效的答案,所以+1,并欢迎来到StackOverflow。 - Mr.Wizard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接