Mathematica 何时创建新符号? [英] When does Mathematica create a new Symbol?

查看:25
本文介绍了Mathematica 何时创建新符号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

美好的一天,

我之前认为 Mathematica 在转换输入字符串(分配给 InString) 到输入表达式(分配给 In).但是一个简单的例子打破了这种解释:

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

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

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

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

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

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

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

正如 Sasha 在对这个问题的评论中注意到的那样,实际上我被默认样式表 Core.nb 中的输出单元的默认 ShowStringCharacters->False 设置欺骗了,并且错过了 DownValues[In]//First 输出的 >FullForm.实际上符号 f 没有用于 In[1] 的定义中,我们也可以通过使用 InputForm 看到:

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]

抱歉仓促发言.

所以现在的问题只是关于 Mathematica 决定创建新的 Symbol 的阶段以及我们如何防止它?例如,在上面的例子中,我们输入 f 作为 SymbolMathematica 将其转换为 String 而不创建新符号.这是 MakeExpression 的内置行为:

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.

我们可以看到增加 $Line 发生在调用 MakeExpression 之前但是新的 Symbol 创建和为 InStringIn 变量在调用 MakeExpression 后发生:

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

我们也可以这样说 $PreRead$NewSymbol 调用时间:

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 执行 afterIn 的新赋值和 after 创建所有新的 Symbol当前$Context中的s:

$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

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

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

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

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

推荐答案

关于您在 edit 部分的问题:不确定这是否是您的想法,但在 FrontEnd 会话中您可以使用$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$`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] 进行检查).例如

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

首先定义 keepSymbolsAsStrings 很重要,以便创建此符号.这使它重入:

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

现在,您可以在解析代码后以您喜欢的方式处理这些符号(保留为字符串).您也可以使用不同的 symbolQ 函数 - 为了举例,我只是使用了一个简单的函数.

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.

但这不适用于包.我没有看到对包执行此操作的直接方法.一种简单的方法是动态重新定义Needs,以类似于预处理阶段的方式在字符串级别修改源,并有效地调用Needs在修改后的源上.但是字符串级别的源代码修改通常很脆弱.

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

编辑

上面的代码有一个缺陷,就是很难区分哪些字符串是字符串,哪些是由上述函数转换的符号.您可以通过将 ClearAll[keepSymbolsAsStrings] 更改为 ClearAll[keepSymbolsAsStrings, symbol]StringJoin["\"", x, "\"" 来修改上面的代码] 通过 RowBox[{"s​​ymbol", "[", StringJoin["\"", x, "\""], "]"}] 跟踪哪些字符串在结果表达式中对应于转换后的符号.

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.

编辑 2

这是修改后的代码,基于 MakeExpression 而不是 $PreRead,正如@Alexey 所建议的:

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

我们需要 Todd Gayley 的技巧打破 MakeExpression 定义中的无限递归.再次举个例子:

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

这种方法更简洁,因为 $PreRead 仍然可供最终用户使用.

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

这篇关于Mathematica 何时创建新符号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆