Когда Mathematica создает новый Символ? - PullRequest
4 голосов
/ 11 апреля 2011

Добрый день,

Ранее я думал, что 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]= {}

Вы можете видеть, что в $ContextPath нет символа f, хотя он уже используется внутри определения для In[1].

Этот пример показывает, что в Mathematica в принципе возможно создавать определения с символами, которых нет в $ContextPath, без их создания.Это может быть интересной альтернативой способу избежать создания символа с использованием Symbol:

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

Может кто-нибудь объяснить, при каких условиях и на каком этапе процесса оценки Mathematica создает новые символы?

РЕДАКТИРОВАТЬ

Как Саша заметил в комментарии к этому вопросу, в действительности я был подделан по умолчанию ShowStringCharacters->False настроек для ячеек вывода в таблице стилей по умолчаниюCore.nb и пропустил 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, но создание и присвоение нового значения для InString и In переменных происходит после вызова 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 и после создания всех новых Symbol с в текущем $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 создаются после вызова $PreRead, MakeExpression и $NewSymbol, но до вызова $Pre.

Ответы [ 3 ]

5 голосов
/ 11 апреля 2011

Относительно вашего вопроса в части edit : не уверен, что это то, что вы имели в виду, но в сеансах 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 для модифицированного источника. Но модификации источника на уровне строки обычно хрупки.

НТН

Редактировать

Приведенный выше код имеет недостаток в том, что трудно различить, какие строки должны были быть строками, а какие были символами, преобразованными вышеуказанной функцией. Вы можете изменить приведенный выше код, изменив 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]
]

Нам нужен трюк Тодда Гейли, чтобы вырваться из бесконечной рекурсии в определениях 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 все еще доступен для конечного пользователя.

4 голосов
/ 11 апреля 2011

Вы можете использовать $NewSymbol и $NewMessage, чтобы лучше понять, когда создается символ. Но из виртуальной книги символ создается в $Context, когда его нельзя найти ни в $Context, ни в $ContextPath.

2 голосов
/ 11 апреля 2011

Я думаю, что ваше базовое понимание того, что символы создаются при разборе входных данных в выражение, является правильным.

Тонкая часть состоит в том, что ? в начале строки (и << и >>) анализируется специально, чтобы разрешить строки без кавычек. (Здесь неявными строками являются шаблоны типа *Min* для ? и имена файлов для << и >>.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...