您好,
之前我认为Mathematica在将输入字符串(分配给InString
)转换为输入表达式(分配给In
)的阶段会在当前$Context
中创建新符号。但一个简单的例子打破了这个解释:
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]= {}
你可以看到,在
In[1]
的定义中已经使用了符号f
,但在$ContextPath
中没有它的符号。
这个例子表明,在Mathematica中原则上可以使用不存在于$ContextPath
中的符号进行定义,而不必创建它们。这可能是避免使用Symbol
方法创建符号的有趣替代方法:
In[9]:= ff := Symbol["f"]
Names["`*"]
Out[10]= {"ff"}
有人能解释一下在哪些条件和评估过程的哪个阶段,Mathematica会创建新符号吗?
编辑
正如Sasha在对这个问题的评论中注意到的那样,在默认样式表Core.nb中,输出单元格的默认设置为ShowStringCharacters->False
,我被愚弄了,并错过了DownValues[In]//First
的输出的FullForm
。实际上,我们可以通过使用InputForm
来看到,在In[1]
的定义中没有使用符号f
:
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]]
可能可以定义某种语法结构,以防止在评估时创建符号。
关于创建新符号的评估阶段
我们可以看到,在调用MakeExpression
之前会增加$Line
,但是在调用MakeExpression
后才会创建新的Symbol
并分配新值给InString
和In
变量:
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
同样的,我们也可以这样说
$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
$Pre
在新的赋值给 In
变量之后执行,且在当前 $Context
中创建所有新的 Symbol
之后执行:
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
似乎无法拦截为
In
变量赋新值。
结论:在调用
$PreRead
、MakeExpression
和$NewSymbol
之后,但在调用$Pre
之前创建了新的Symbol
。
FullForm
。这意味着在定义In
时,实际上没有使用符号f
,这与我之前的想法不同。但你能否评论一下我对符号创建的理解(在问题的第一段中描述)是否正确? - Alexey Popkov