【问题标题】:When does Mathematica create a new Symbol?Mathematica 何时创建新符号?
【发布时间】:2011-04-11 05:07:04
【问题描述】:

早安,

我之前认为 Mathematica 在将输入字符串(分配给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]= {}

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

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

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

谁能解释在评估过程的哪个条件和哪个阶段Mathematica创建新符号?

编辑

正如 Sasha 在对这个问题的评论中注意到的那样,实际上我被默认样式表 Core.nb 中的输出单元格的默认设置 ShowStringCharacters->False 欺骗了,并且错过了 DownValues[In]//First 输出的 FullForm。在真正的符号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 变量分配新值发生在调用 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

$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 中创建所有新Symbols 之后:

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

好像是it is not possible to intercept assigning new value for In variable


结论:新的Symbols 是在调用$PreReadMakeExpression$NewSymbol 之后但在调用$Pre 之前创建的。

【问题讨论】:

  • Doing DownValues[In] // First // FullForm 我看到 RuleDelayed[HoldPattern[In[1]],Information["f",Rule[LongForm,False]]]]
  • @Sasha 你说得对,我错过了FullForm。这意味着实际上符号f 没有像我之前想的那样在In 的定义中使用。但是您能否评论一下我对符号创建的理解(在问题的第一段中描述)对吗?

标签: wolfram-mathematica


【解决方案1】:

关于您在 edit 部分中的问题:不确定这是否是您的想法,但在前端会话中,您可以使用 $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,而不是@Alexey 建议的$PreRead

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

【讨论】:

  • 感谢您提供这个聪明的解决方案!但是,为MakeExpression 重新定义的方法可能会产生相同的开销,同时将$PreRead 留给用户免费使用。
  • 确实,使用MakeExpression 听起来像是一个更干净的选择,如果你可以让它工作(我没有尝试),特别是如果你不是最终用户。
  • 从 Simon 对我的 recent answer 的编辑中看到,MakeExpression 出现问题的主要未记录原因是它必须返回 Hold* 表达式。
  • @Alexey 感谢您的链接,非常有趣。我有一个印象,通常MakeExpression 将结果包装在HoldComplete 而不是Hold。无论如何,我已经相应地更新了我的代码。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    微妙之处在于,行首的?(以及<<>>)专门解析以允许字符串不需要引号。 (这里的隐式字符串是*Min* 之类的模式?<<>> 的文件名。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-18
      • 2014-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 2011-05-24
      相关资源
      最近更新 更多