美好的一天,
我之前认为 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
创建并在调用{InString
和In
变量后分配新值{1}}:
MakeExpression
关于$PreRead
和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
通话时间我们可以这么说:
$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。
结论:在致电$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
和Symbol
之后,但在致电$PreRead
之前,我们会创建新的MakeExpression
。
答案 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*
?
以及<<
和>>
的文件名等模式。)