parsing - 以 `FullForm` 语法保存 Mathematica 代码

标签 parsing syntax wolfram-mathematica metaprogramming

我需要在大型 Mathematica 代码库(数十万行代码)上进行一些元编程,并且不想编写一个成熟的解析器,所以我想知道如何最好地从Mathematica 笔记本采用易于解析的语法。

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

documentation for 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 因错误而终止。因此,我希望用更合适的语言进行元编程。

编辑

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

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

最佳答案

你当然可以做到这一点。这是一种方法:

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 有许多技术可用于进行元编程和 avoid过早的评价。我想到的一个我在 this 中描述过答案,但还有很多其他的。由于您可以在 Mathematica 中操作已解析的代码并使用模式匹配,因此您可以节省很多。您可以浏览 SO Mathematica 标签(过去的问题)并找到大量元编程和评估控制的示例。

编辑

通过自动评估符号来减轻您的痛苦(实际上只有几个,Infinity就是其中之一)。如果您只需要获取给定符号的符号名称,那么这功能将有所帮助:

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

你用它作为

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

或者,您可以通过 SetAttributesHoldFirst 属性简单地添加到 SymbolName 函数。一种方法是在全局范围内执行此操作:

SetAttributes[符号名称,HoldFirst]; SymbolName[Infinity]//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}]]

在这种情况下进行替换的一般方法是使用保留属性(请参阅 this 答案)和保留表达式内的替换(请参阅 this 问题)。对于手头的情况:

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

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

,尽管这不是唯一的方法。除了使用上面的宏,我们还可以将对 SymbolName 的修改编码到规则本身中(这里我使用更冗长的就地评估形式(Trott - Strzebonski 技巧),但是您可以也使用 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"}]]

实际上,整个答案很好地演示了各种元编程技术。根据我自己的经验,我可以引导您访问this , this , this , thisthis我的答案,其中元编程对于解决我正在解决的问题至关重要。您还可以通过 Mathematica 中所有函数都带有 Hold 属性的函数比例来判断 - 如果我没记错的话,大约是 10-15%。所有这些函数实际上都是宏,在代码上运行。对我来说,这是一个非常有代表性的事实,它告诉我 Mathematica 非常好地建立在其元编程工具的基础上。

关于parsing - 以 `FullForm` 语法保存 Mathematica 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8281858/

相关文章:

c++ - 理解 "const"在声明中的位置

c - 在没有 strtok/lexer 的情况下将字符串解析为标记

html - 如何在一大段文本中查找/突出显示不属于允许标签列表之一的 HTML 标签?

objective-c - NSManagedObject 属性值的 NSNull 处理

wolfram-mathematica - 在mathematica中选择特定的符号定义(而不是转换规则)

wolfram-mathematica - 如何在 Mathematica Notebook Styles 中填写 CellFrameLabels?

wolfram-mathematica - 如何求解数学中递推关系的解析解

java - JSON键是否需要唯一?

arrays - Kotlin 中简洁的 2D 原始数组初始化

algorithm - IBM 研究论文中的未知语法