用`FullForm`语法保存Mathematica代码

Jon*_*rop 7 syntax parsing wolfram-mathematica metaprogramming

我需要在一个大的Mathematica代码库(数十万行代码)上做一些元编程,并且不想编写一个完整的解析器,所以我想知道如何最好地从Mathematica笔记本中获取代码用一种易于解析的语法.

是否可以在FullForm语法中导出Mathematica笔记本,或者在语法中保存所有定义FullForm

文档Save说,它可以在唯一的出口InputForm语法,这是不平凡的解析.

到目前为止,我所拥有的最佳解决方案是评估笔记本,然后使用DownValues参数提取重写规则(但这会错过符号定义),如下所示:

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

到目前为止,我尝试了各种方法,但没有一种方法效果很好.Mathematica中的元编程似乎非常困难,因为它一直在评估我想要保持不被评估的事物.例如,我想获取无穷大符号的字符串名称,SymbolName[Infinity]但是将Infinity得到的值计算为非符号,并且调用SymbolName带有错误的die.因此,我希望用更合适的语言进行元编程.

编辑

最好的解决方案似乎是手动将笔记本保存为包(.m)文件,然后使用以下代码进行翻译:

stream = OpenWrite["EverythingFullForm.m"];
WriteString[stream, Import["Everything.m", "HeldExpressions"] // FullForm];
Close[stream];
Run Code Online (Sandbox Code Playgroud)

Leo*_*rin 6

你当然可以这样做.这是一种方式:

exportCode[fname_String] := 
 Function[code, 
    Export[fname, ToString@HoldForm@FullForm@code, "String"], 
    HoldAllComplete]
Run Code Online (Sandbox Code Playgroud)

例如:

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

并将其作为字符串导入:

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]"
Run Code Online (Sandbox Code Playgroud)

然而,对于Mathematica来说,使用其他语言进行元编程对我来说听起来很荒谬,因为Mathematica非常适合这种情况.Mathematica中有许多技术可用于元编程并 避免过早评估.我在这个答案中描述了一个我想到的,但还有很多其他的.由于您可以对已解析的代码进行操作并在Mathematica中使用模式匹配,因此可以节省很多.您可以浏览SO Mathematica标签(过去的问题),并找到许多元编程和评估控制的例子.

编辑

使用自动评估符号来缓解您的痛苦(实际上只有少数符号,Infinity就是其中之一).如果您只需要获取给定符号的符号名称,那么此函数将有助于:

unevaluatedSymbolName =  Function[sym, SymbolName@Unevaluated@sym, HoldAllComplete]
Run Code Online (Sandbox Code Playgroud)

你用它作为

In[638]:= unevaluatedSymbolName[Infinity]//InputForm
Out[638]//InputForm="Infinity"
Run Code Online (Sandbox Code Playgroud)

或者,你可以简单地添加HoldFirst属性,SymbolName通过功能SetAttributes.一种方法是全球范围内:

SetAttributes [符号名称,HoldFirst]; 符号名称[无限远] // InputForm

然而,全局修改内置函数是危险的,因为它可能对Mathematica这样的大型系统产生不可预测的影响:

ClearAttributes[SymbolName, HoldFirst];
Run Code Online (Sandbox Code Playgroud)

这是一个在本地使用的宏:

ClearAll[withUnevaluatedSymbolName];
SetAttributes[withUnevaluatedSymbolName, HoldFirst];
withUnevaluatedSymbolName[code_] :=
  Internal`InheritedBlock[{SymbolName},
     SetAttributes[SymbolName, HoldFirst];
     code]
Run Code Online (Sandbox Code Playgroud)

现在,

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

Out[649]//InputForm=  {"Infinity", 8}
Run Code Online (Sandbox Code Playgroud)

您可能还希望在一段代码中进行一些替换,例如,用名称替换给定的符号.这是一个示例代码(我将其包装Hold以防止评估):

c = Hold[Integrate[Exp[-x^2], {x, -Infinity, Infinity}]]
Run Code Online (Sandbox Code Playgroud)

在这种情况下进行替换的一般方法是使用Hold-attributes(请参阅答案)和保留表达式中的替换(请参阅问题).对于手头的情况:

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

Out[652]//InputForm=
Hold[Integrate[Exp[-x^2], {x, -"Infinity", "Infinity"}]]
Run Code Online (Sandbox Code Playgroud)

虽然这不是唯一的方法.除了使用上面的宏,我们还可以将修改编码SymbolName到规则本身(这里我使用的是更加冗长的形式(Trott - Strzebonski技巧)的就地评估,但你也可以使用RuleCondition:

ClearAll[replaceSymbolUnevaluatedRule];
SetAttributes[replaceSymbolUnevaluatedRule, HoldFirst];
replaceSymbolUnevaluatedRule[sym_Symbol] :=
  HoldPattern[sym] :> With[{eval = SymbolName@Unevaluated@sym}, eval /; True];
Run Code Online (Sandbox Code Playgroud)

现在,例如:

In[629]:= 
Hold[Integrate[Exp[-x^2],{x,-Infinity,Infinity}]]/.
      replaceSymbolUnevaluatedRule[Infinity]//InputForm
Out[629]//InputForm=
    Hold[Integrate[Exp[-x^2], {x, -"Infinity", "Infinity"}]]
Run Code Online (Sandbox Code Playgroud)

实际上,这整个答案很好地演示了各种元编程技术.从我自己的经验,我可以直接给你这个,这个,这个,这个这个矿的答案,其中元编程是必不可少的,以解决问题,我解决.您还可以通过Mathematica中保持属性的所有函数的函数分数来判断 - 如果内存对我有好处,则大约为10-15%.所有这些函数都是有效的宏,在代码上运行.对我而言,这是一个非常具有指示性的事实,告诉我Mathematica犹豫不决地建立在其元编程设施上.


WRe*_*ach 5

表达式的完整形式可以从被提取CodeInput一笔记本的细胞如下:

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

$exprs被赋予表达式读取,包装Hold以防止评估. $exprs然后可以保存到文本文件中:

Export["myfile.txt", ToString[$exprs]]
Run Code Online (Sandbox Code Playgroud)

包文件(.m)以这种方式更容易阅读:

Import["mypackage.m", "HeldExpressions"] //
Flatten[HoldComplete @@ #, 1, HoldComplete] &
Run Code Online (Sandbox Code Playgroud)

  • 我可以用我的一些笔记本重现你的问题.我很难过.我把它带到社区:[解释输入单元格表达式的问题](http://stackoverflow.com/q/8283256/211232) (2认同)

归档时间:

查看次数:

1071 次

最近记录:

14 年,1 月 前