Mathematica何时创造新的符号?

时间:2011-04-11 05:07:05

标签: wolfram-mathematica

美好的一天,

我之前认为 Mathematica 在将输入字符串(分配给$Context)转换为输入表达式的阶段在当前InString中创建新符号(已分配给In)。但是一个简单的例子打破了这个解释:

In[1]:= ?f
During evaluation of In[1]:= Information::notfound: Symbol f not found. >>
In[2]:= Names["`*"]
Out[2]= {}
In[3]:= DownValues[In]//First
InString[1]
Names["`*"]
Out[3]= HoldPattern[In[1]]:>Information[f,LongForm->False]
Out[4]= \(? f\)
Out[5]= {}

您可以看到f中没有符号$ContextPath,尽管它已在In[1]的定义中使用。

此示例显示 Mathematica 原则上可以使用$ContextPath中不存在的符号进行定义而不创建它们。这可能是使用Symbol避免符号创建的方法的有趣替代方法:

In[9]:= ff := Symbol["f"]
Names["`*"]
Out[10]= {"ff"}

有人可以解释评估过程的哪个阶段和哪个阶段 Mathematica 创建新符号?

修改

正如Sasha在这个问题的评论中注意到的那样,我在默认样式表Core.nb中输出单元格的默认ShowStringCharacters->False设置被欺骗,并且错过了输出的FullForm DownValues[In]//First。实际上,f的定义中没有使用In[1],因为我们也可以使用InputForm看到:

In[1]:= ?f
DownValues[In]//First//InputForm
During evaluation of In[1]:= Information::notfound: Symbol f not found. >>
Out[2]//InputForm=
HoldPattern[In[1]] :> Information["f", LongForm -> False]

抱歉草率声明。

所以现在的问题是 Mathematica 决定创建新Symbol以及我们如何阻止它的阶段? 例如,在上面的示例中,我们将f输入为Symbol,但 Mathematica 将其转换为String而不创建新符号。这是MakeExpression

的内置行为
In[1]:= ?f
InputForm[MakeExpression[ToExpression@InString[1], StandardForm]]

During evaluation of In[1]:= Information::notfound: Symbol f not found. >>

Out[2]//InputForm=
HoldComplete[Information["f", LongForm -> False]]

可能有可能定义某种类型的句法结构,它会阻止符号的产生直到评估时间。

关于创建新符号时的评估阶段

我们可以看到,在调用$Line之前会增加MakeExpression,但新的Symbol创建并在调用{InStringIn变量后分配新值{1}}:

MakeExpression

关于$PreReadIn[1]:= MakeExpression[My`boxes_,My`f_]/;!TrueQ[My`$InsideMakeExpression]:=Block[{My`$InsideMakeExpression=True},Print[$Line];Print[DownValues[InString][[All,1]]];Print[DownValues[In][[All,1]]];Print[Names["`*"]];MakeExpression[My`boxes,My`f]]; In[2]:= a During evaluation of In[2]:= 2 During evaluation of In[2]:= {HoldPattern[InString[1]]} During evaluation of In[2]:= {HoldPattern[In[1]]} During evaluation of In[2]:= {} Out[2]= a 通话时间我们可以这么说:

$NewSymbol

In[1]:= $NewSymbol:=Print["Names[\"`*\"]=",Names["`*"],"\nDownValues[InString]=",DownValues[InString][[All,1]],"\nDownValues[In]=",DownValues[In][[All,1]],"\nName: ",#1,"\tContext: ",#2]& In[2]:= a During evaluation of In[2]:= Names["`*"]={} DownValues[InString]={HoldPattern[InString[1]]} DownValues[In]={HoldPattern[In[1]]} Name: a Context: Global` Out[2]= a 在完成对$Pre的新作业后执行 在当前{{>创建所有新In之后执行 1}}:

Symbol

似乎是it is not possible to intercept assigning new value for In variable


结论:在致电$ContextIn[1]:= $Pre := (Print[Names["`*"]]; Print[DownValues[In][[All, 1]]]; ##) & In[2]:= a During evaluation of In[2]:= {a} During evaluation of In[2]:= {HoldPattern[In[1]],HoldPattern[In[2]]} Out[2]= a Symbol之后,但在致电$PreRead之前,我们会创建新的MakeExpression

3 个答案:

答案 0 :(得分:5)

关于编辑部分中的问题:不确定这是否是您的想法,但在FrontEnd会话中,您可以使用$PreRead在解析阶段将符号保留为字符串。这是一个可行的黑客:

symbolQ = StringMatchQ[#, RegularExpression["[a-zA-Z$][a-zA-Z$`0-9]*"]] &;

ClearAll[keepSymbolsAsStrings];
SetAttributes[keepSymbolsAsStrings, HoldAllComplete];

$PreRead  = # //. RowBox[{"keepSymbolsAsStrings", rest___}] :>
 RowBox[{"keepSymbolsAsStrings", 
   Sequence @@ ({rest} //. x_String?symbolQ :>
       With[{context = Quiet[Context[x]]},            
        StringJoin["\"", x, "\""] /; 
         Head[context] === Context])}] &;

只有当符号尚不存在时(通过Context[symbol_string_name]检查),该符号才会转换为字符串。例如

In[4]:= keepSymbolsAsStrings[a+b*Sin[c]]//FullForm

Out[4]//FullForm= keepSymbolsAsStrings[Plus["a",Times["b",Sin["c"]]]]

首先定义keepSymbolsAsStrings非常重要,以便创建此符号。这使它重新进入:

In[6]:= keepSymbolsAsStrings[a+b*Sin[c]*keepSymbolsAsStrings[d+e*Sin[f]]]//FullForm

Out[6]//FullForm= 
  keepSymbolsAsStrings[Plus["a",Times["b",Sin["c"],
  keepSymbolsAsStrings[Plus["d",Times["e",Sin["f"]]]]]]]

现在,您可以按照自己喜欢的方式解析代码后处理这些符号(保存为字符串)。您也可以使用不同的symbolQ函数 - 为了示例,我只使用一个简单的函数。

但这不适用于包。我没有看到一个简单的方法来执行此包。一种简单的方法是动态地重新定义Needs,以类似于某种预处理阶段的方式修改字符串级别的源,并在修改后的源上有效地调用Needs。但字符串级源修改通常很脆弱。

HTH

修改

上面的代码有一个缺陷,即很难区分哪些字符串是字符串,哪些字符串是由上述函数转换的符号。您可以修改上述代码,方法是将ClearAll[keepSymbolsAsStrings]更改为ClearAll[keepSymbolsAsStrings, symbol],将StringJoin["\"", x, "\""]更改为RowBox[{"symbol", "[", StringJoin["\"", x, "\""], "]"}],以跟踪结果表达式中哪些字符串与转换后的符号相对应。

修改2

以下是基于MakeExpression而非$PreRead的修改后的代码,正如@Alexey所建议的那样:

symbolQ =  StringMatchQ[#, RegularExpression["[a-zA-Z$][a-zA-Z$0-9`]*"]] &;

ClearAll[keepSymbolsAsStrings, symbol];
SetAttributes[keepSymbolsAsStrings, HoldAllComplete];

Module[{tried},
 MakeExpression[RowBox[{"keepSymbolsAsStrings", rest___}], form_] :=
  Block[{tried = True},
    MakeExpression[
       RowBox[{"keepSymbolsAsStrings", 
         Sequence @@ ({rest} //. x_String?symbolQ :>
            With[{context = Quiet[Context[x]]},            
             RowBox[{"symbol", "[", StringJoin["\"", x, "\""], "]"}] /;
             Head[context] === Context])}], form]
  ] /;!TrueQ[tried]
]

我们需要Todd Gayley的trick来摆脱MakeExpression定义中的无限递归。以下是这些例子:

In[7]:= keepSymbolsAsStrings[a+b*Sin[c]]//FullForm

Out[7]//FullForm= keepSymbolsAsStrings[Plus[symbol["a"],Times[symbol["b"],Sin[symbol["c"]]]]]

In[8]:= keepSymbolsAsStrings[a+b*Sin[c]*keepSymbolsAsStrings[d+e*Sin[f]]]//FullForm

Out[8]//FullForm=  keepSymbolsAsStrings[Plus[symbol["a"],Times[symbol["b"],Sin[symbol["c"]],
keepSymbolsAsStrings[Plus[symbol["d"],Times[symbol["e"],Sin[symbol["f"]]]]]]]]

此方法更清晰,因为$PreRead仍可供最终用户使用。

答案 1 :(得分:4)

您可以使用$NewSymbol$NewMessage更好地了解创建符号的时间。但是,从虚拟书籍中,当$Context$Context都找不到符号时,该符号会在$ContextPath中创建。

答案 2 :(得分:2)

我认为您在将输入解析为表达式时创建符号的基本理解是正确的。

细微的部分是?在行的开头(<<>>)专门解析以允许字符串不需要引号。 (这里的隐式字符串是*Min* ?以及<<>>的文件名等模式。)