Mathematica何时创造新的符号?

Ale*_*kov 4 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]= {}
Run Code Online (Sandbox Code Playgroud)

您可以看到虽然已经在内部定义中使用了符号f,$ContextPath但它没有符号In[1].

这个例子表明Mathematica原则上可以用$ContextPath不创建它们的符号来定义.这可能是使用Symbol以下方法避免符号创建的有趣替代方法:

In[9]:= ff := Symbol["f"]
Names["`*"]
Out[10]= {"ff"}
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释Mathematica在哪些条件和评估过程的哪个阶段创建新符号?

编辑

至于萨沙已经注意到这个问题的评论,在我真的被欺骗的默认ShowStringCharacters->False设置为默认的样式表Core.nb输出单元,错过了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]
Run Code Online (Sandbox Code Playgroud)

抱歉草率声明.

所以现在的问题是Mathematica决定创造新的阶段Symbol以及我们如何防止它的阶段?例如,在上面的示例中,我们输入fas,SymbolMathematica将其转换为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]]
Run Code Online (Sandbox Code Playgroud)

可能有可能定义某种类型的句法结构,它将阻止符号创建直到评估时间.

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

我们可以看到$Line在调用之前发生了递增,MakeExpression但是在调用之后发生了新的Symbol创建并为其分配了新的值InStringIn变量MakeExpression:

In[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
Run Code Online (Sandbox Code Playgroud)

我们可以说$PreRead$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
Run Code Online (Sandbox Code Playgroud)

$Pre在进行新的赋值执行In以及Symbol当前创建所有新的s后执行$Context:

In[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
Run Code Online (Sandbox Code Playgroud)

似乎无法拦截为In变量分配新值.


结论:新Symbols的调用之后创建$PreRead,MakeExpression$NewSymbol但之前调用$Pre.

Leo*_*rin 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])}] &;
Run Code Online (Sandbox Code Playgroud)

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

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

Out[4]//FullForm= keepSymbolsAsStrings[Plus["a",Times["b",Sin["c"]]]]
Run Code Online (Sandbox Code Playgroud)

首先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"]]]]]]]
Run Code Online (Sandbox Code Playgroud)

现在,您可以按照自己喜欢的方式在解析代码后处理这些符号(保存为字符串).你也可以使用不同的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]
]
Run Code Online (Sandbox Code Playgroud)

我们需要 Todd Gayley 的技巧来摆脱定义中的无限递归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"]]]]]]]]
Run Code Online (Sandbox Code Playgroud)

此方法更清晰,因为$PreRead最终用户仍然可以使用.