Сохранить код Mathematica в синтаксисе `FullForm` - PullRequest
7 голосов
/ 27 ноября 2011

Мне нужно немного метапрограммировать большую базу кода Mathematica (сотни тысяч строк кода), и мне не нужно писать полноценный парсер, поэтому мне было интересно, как лучше всего получить код из Записная книжка Mathematica в легко разбираемом синтаксисе.

Можно ли экспортировать записную книжку Mathematica с синтаксисом FullForm или сохранить все определения с синтаксисом FullForm?

В документации для Save говорится, что он может экспортировать только в синтаксисе InputForm, который нетривиален для анализа.

Лучшее решение, которое у меня есть, - это оценить записную книжку, а затем использовать DownValues для извлечения правил перезаписи с аргументами (но при этом отсутствуют определения символов) следующим образом:

DVs[_] := {}
DVs[s_Symbol] := DownValues[s]
stream = OpenWrite["FullForm.m"];
WriteString[stream, 
  DVs[Symbol[#]] & /@ Names["Global`*"] // Flatten // FullForm];
Close[stream];

До сих пор я пробовал множество подходов, но ни один из них не работает хорошо. Метапрограммирование в Mathematica кажется чрезвычайно сложным, потому что оно продолжает оценивать вещи, которые я хочу оставить без оценки. Например, я хотел получить строковое имя символа бесконечности, используя SymbolName[Infinity], но Infinity преобразуется в не-символ, и вызов SymbolName умирает с ошибкой. Отсюда мое желание заняться метапрограммированием на более подходящем языке.

EDIT

Лучшее решение - сохранить записные книжки в виде файлов (.m) вручную, а затем перевести их с помощью следующего кода:

stream = OpenWrite["EverythingFullForm.m"];
WriteString[stream, Import["Everything.m", "HeldExpressions"] // FullForm];
Close[stream];

Ответы [ 2 ]

6 голосов
/ 27 ноября 2011

Вы, конечно, можете сделать это. Вот один из способов:

exportCode[fname_String] := 
 Function[code, 
    Export[fname, ToString@HoldForm@FullForm@code, "String"], 
    HoldAllComplete]

Например:

fn = exportCode["C:\\Temp\\mmacode.m"];
fn[
  Clear[getWordsIndices];
  getWordsIndices[sym_, words : {__String}] := 
      Developer`ToPackedArray[words /. sym["Direct"]];
];

И импортировать это как строку:

In[623]:= Import["C:\\Temp\\mmacode.m","String"]//InputForm
Out[623]//InputForm=
"CompoundExpression[Clear[getWordsIndices], SetDelayed[getWordsIndices[Pattern[sym, Blank[]], \
Pattern[words, List[BlankSequence[String]]]], Developer`ToPackedArray[ReplaceAll[words, \
sym[\"Direct\"]]]], Null]"

Однако, переход на другой язык для выполнения метапрограммирования для Mathematica звучит для меня нелепо, учитывая, что Mathematica очень хорошо подходит для этого. В Mathematica доступно множество методов для выполнения метапрограммирования и избежания преждевременной оценки. Тот, что приходит мне в голову, я описал в этом ответе, но есть много других. Поскольку вы можете работать с проанализированным кодом и использовать сопоставление с образцом в Mathematica, вы экономите много . Вы можете просмотреть теги SO Mathematica (прошлые вопросы) и найти множество примеров метапрограммирования и контроля оценки.

EDIT

Чтобы облегчить вашу боль с помощью автоматической оценки символов (на самом деле их всего несколько, Infinity является одним из них). Если вам просто нужно получить имя символа для данного символа, то эта функция поможет:

unevaluatedSymbolName =  Function[sym, SymbolName@Unevaluated@sym, HoldAllComplete]

Вы используете его как

In[638]:= unevaluatedSymbolName[Infinity]//InputForm
Out[638]//InputForm="Infinity"

Кроме того, вы можете просто добавить атрибут HoldFirst к функции SymbolName через SetAttributes. Один из способов - сделать это глобально:

* +1033 * SetAttributes [SymbolName, HoldFirst]; SymbolName [Бесконечность] // InputForm

Глобальное изменение встроенных функций, однако, опасно, поскольку может привести к непредсказуемым последствиям для такой большой системы, как Mathematica:

ClearAttributes[SymbolName, HoldFirst];

Вот макрос для локального использования:

ClearAll[withUnevaluatedSymbolName];
SetAttributes[withUnevaluatedSymbolName, HoldFirst];
withUnevaluatedSymbolName[code_] :=
  Internal`InheritedBlock[{SymbolName},
     SetAttributes[SymbolName, HoldFirst];
     code]

Теперь

In[649]:= 
withUnevaluatedSymbolName[
   {#,StringLength[#]}&[SymbolName[Infinity]]]//InputForm

Out[649]//InputForm=  {"Infinity", 8}

Вы также можете сделать некоторые замены в фрагменте кода, скажем, заменить данный символ на его имя. Вот пример кода (который я обертываю в Hold, чтобы предотвратить его оценку):

c = Hold[Integrate[Exp[-x^2], {x, -Infinity, Infinity}]]

Общий способ сделать замены в таких случаях - использовать атрибуты Hold (см. этот ответ) и замены внутри удерживаемых выражений (см. этот вопрос). Для данного случая:

In[652]:= 
withUnevaluatedSymbolName[
       c/.HoldPattern[Infinity]:>RuleCondition[SymbolName[Infinity],True]
]//InputForm

Out[652]//InputForm=
Hold[Integrate[Exp[-x^2], {x, -"Infinity", "Infinity"}]]

, хотя это не единственный способ сделать это. Вместо того, чтобы использовать вышеупомянутый макрос, мы также можем закодировать модификацию SymbolName в самом правиле (здесь я использую более многословную форму (трюк Тротта - Стржебонски) для оценки на месте, но вы можете использовать RuleCondition как хорошо:

ClearAll[replaceSymbolUnevaluatedRule];
SetAttributes[replaceSymbolUnevaluatedRule, HoldFirst];
replaceSymbolUnevaluatedRule[sym_Symbol] :=
  HoldPattern[sym] :> With[{eval = SymbolName@Unevaluated@sym}, eval /; True];

Теперь, например:

In[629]:= 
Hold[Integrate[Exp[-x^2],{x,-Infinity,Infinity}]]/.
      replaceSymbolUnevaluatedRule[Infinity]//InputForm
Out[629]//InputForm=
    Hold[Integrate[Exp[-x^2], {x, -"Infinity", "Infinity"}]]

На самом деле, весь этот ответ является хорошей демонстрацией различных методов метапрограммирования. Исходя из своего собственного опыта, я могу направить вас к этому , этому , этому , этому и этому ответам мой, где метапрограммирование было важно для решения проблемы, которую я решал. Вы также можете судить по доле функций в Mathematica, несущих атрибуты Hold ко всем функциям - это около 10-15 процентов, если память мне хорошо служит. Все эти функции по сути являются макросами, работающими над кодом. Для меня это очень показательный факт, говорящий мне, что Mathematica активно использует свои возможности метапрограммирования.

5 голосов
/ 27 ноября 2011

Полные формы выражений можно извлечь из ячеек Code и Input тетради следующим образом:

$exprs =    
  Cases[
    Import["mynotebook.nb", "Notebook"]
  , Cell[content_, "Code"|"Input", ___] :>
      ToExpression[content, StandardForm, HoldComplete]
  , Infinity
  ] //
  Flatten[HoldComplete @@ #, 1, HoldComplete] & //
  FullForm

$exprs присваивается прочитанное выражение, заключенное в Hold для предотвращения оценки. $exprs может быть сохранено в текстовом файле:

Export["myfile.txt", ToString[$exprs]]

Файлы пакетов (.m) немного легче читать таким образом:

Import["mypackage.m", "HeldExpressions"] //
Flatten[HoldComplete @@ #, 1, HoldComplete] &
...