When does Mathematica create a new Symbol?

爷,独闯天下 提交于 2019-12-05 13:18:50
Leonid Shifrin

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

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.

Sasha

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.

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 >>.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!