Относительно вашего вопроса в части 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
все еще доступен для конечного пользователя.