Mathematica 什么时候创建新符号?

发布于 2024-10-31 09:22:22 字数 3816 浏览 0 评论 0 原文

美好的一天,

我之前认为 Mathematica 在转换输入字符串(分配给 InString$Context 中创建新符号code>) 来输入表达式(分配给 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。实际上,In[1] 的定义中并未使用符号 f,正如我们也可以通过使用 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 决定创建新符号的阶段以及我们如何阻止它? 例如,在上面的示例中,我们输入 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中的符号

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 变量分配新值


结论:新的 Symbol 是在调用 $PreReadMakeExpression$NewSymbol 之后、调用 之前创建的>$Pre

Good day,

I thought earlier that Mathematica creates new symbols in the current $Context at the stage of converting of the input string (that is assigned to InString) to input expression (that is assigned to In). But one simple example has broken this explanation:

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]= {}

You can see that there is no symbol f in the $ContextPath although it is already used inside definition for In[1].

This example shows that it is in principle possible in Mathematica to make definitions with symbols that do not exist in the $ContextPath without creating them. This could be interesting alternative to the method of avoiding symbol creation using Symbol:

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

Can anybody explain at which conditions and at which stage of the evaluation process Mathematica creates new Symbols?

EDIT

As Sasha have noticed in the comment to this question, in really I was spoofed by default ShowStringCharacters->False settings for the Output cells in the default stylesheet Core.nb and missed the FullForm of the output for DownValues[In]//First. In really symbol f is not used in the definition for In[1] as we can see also by using 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]

Sorry for hasty statement.

So the question now is just about the stage at which Mathematica decides to create new Symbol and how we can prevent it?
For example, in the above example we input f as Symbol but Mathematica converts it to String without creating new symbol. This is built-in behavior of 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]]

Probably it is possible to define some type of syntactic construct that will prevent symbol creation until the evaluation time.

About stage of evaluation when new symbol is created

We can see that incrementing $Line happens before calling MakeExpression but new Symbol creation and assigning new value for InString and In variables happens after calling 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

The same we can say about $PreRead and $NewSymbol call time:

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 executes after new assignment to In is made and after creating all new Symbols in the current $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

It seems that it is not possible to intercept assigning new value for In variable.


The conclusion: new Symbols are created after calling $PreRead, MakeExpression and $NewSymbol but before calling $Pre.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

起风了 2024-11-07 09:22:22

关于您在 edit 部分中的问题:不确定这是否是您的想法,但在前端会话中,您可以使用 $PreRead 在解析过程中将符号保留为字符串阶段。这是一种可能的技巧:

symbolQ = StringMatchQ[#, RegularExpression["[a-zA-Z$][a-zA-Z

关于您在 edit 部分中的问题:不确定这是否是您的想法,但在前端会话中,您可以使用 $PreRead 在解析过程中将符号保留为字符串阶段。这是一种可能的技巧:

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

Edit

上面的代码有一个缺陷,就是很难区分哪些字符串是字符串,哪些是上面函数转换的符号。您可以修改上面的代码,将 ClearAll[keepSymbolsAsStrings] 更改为 ClearAll[keepSymbolsAsStrings, symbol]StringJoin["\"", x, "\"" ] 通过 RowBox[{"s​​ymbol", "[", 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]
]

我们需要 技巧打破了 < 定义中的无限递归代码>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

Regarding your question in the edit part: not sure if this is what you had in mind , but in FrontEnd sessions you can use $PreRead to keep symbols as strings during the parsing stage. Here is one possible hack which does it:

symbolQ = StringMatchQ[#, RegularExpression["[a-zA-Z$][a-zA-Z

Regarding your question in the edit part: not sure if this is what you had in mind , but in FrontEnd sessions you can use $PreRead to keep symbols as strings during the parsing stage. Here is one possible hack which does it:

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])}] &;

The symbol will be converted to string only if it does not exist yet (which is checked via Context[symbol_string_name]). For example

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

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

It is important that the keepSymbolsAsStrings is defined first, so that this symbol is created. This makes it re-entrant:

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"]]]]]]]

Now, you can handle these symbols (kept as strings) after your code has been parsed, in the way you like. You could also use a different symbolQ function - I just use a simple-minded one for the sake of example.

This won't work for packages though. I don't see a straightforward way to do this for packages. One simplistic approach would be to dynamically redefine Needs, to modify the source on the string level in a similar manner as a sort of a pre-processing stage, and effectively call Needs on the modified source. But string-level source modifications are generally fragile.

HTH

Edit

The above code has a flaw in that it is hard to distinguish which strings were meant to be strings and which were symbols converted by the above function. You can modify the code above by changing ClearAll[keepSymbolsAsStrings] to ClearAll[keepSymbolsAsStrings, symbol] and StringJoin["\"", x, "\""] by RowBox[{"symbol", "[", StringJoin["\"", x, "\""], "]"}] to keep track of which strings in the resulting expression correspond to converted symbols.

Edit 2

Here is the modified code, based on MakeExpression rather than $PreRead, as suggested by @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]
]

We need the trick of Todd Gayley to break from an infinite recursion in definitions of MakeExpression. Here are the examples again:

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"]]]]]]]]

This method is cleaner since $PreRead is still available to the end user.

佞臣 2024-11-07 09:22:22

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

You can use $NewSymbol and $NewMessage to have a better understanding when the symbol is created. But from the virtual book, the symbol is created in $Context when it can be found in neither $Context nor $ContextPath.

讽刺将军 2024-11-07 09:22:22

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

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

I think your basic understanding that symbols are created when the input is parsed into an expression is correct.

The subtle part is that ? at the beginning of a line (and << and >>) parse specially to allow strings without requiring quotation marks. (The implicit strings here are patterns like *Min* for ? and file names for << and >>.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文