以“FullForm”语法保存 Mathematica 代码

发布于 2024-12-18 02:41:11 字数 1113 浏览 0 评论 0原文

我需要在大型 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];

到目前为止,我已经尝试了多种方法但没有一个运作良好。 Mathematica 中的元编程似乎极其困难,因为它不断评估我想不评估的东西。例如,我想使用 SymbolName[Infinity] 获取无穷大符号的字符串名称,但 Infinity 被评估为非符号并且调用 SymbolName 因错误而终止。因此,我希望用更合适的语言进行元编程。

编辑

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

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

I need to do some metaprogramming on a large Mathematica code base (hundreds of thousands of lines of code) and don't want to have to write a full-blown parser so I was wondering how best to get the code from a Mathematica notebook out in an easily-parsed syntax.

Is it possible to export a Mathematica notebook in FullForm syntax, or to save all definitions in FullForm syntax?

The documentation for Save says that it can only export in the InputForm syntax, which is non-trivial to parse.

The best solution I have so far is to evaluate the notebook and then use DownValues to extract the rewrite rules with arguments (but this misses symbol definitions) as follows:

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

I've tried a variety of approaches so far but none are working well. Metaprogramming in Mathematica seems to be extremely difficult because it keeps evaluating things that I want to keep unevaluated. For example, I wanted to get the string name of the infinity symbol using SymbolName[Infinity] but the Infinity gets evaluated into a non-symbol and the call to SymbolName dies with an error. Hence my desire to do the metaprogramming in a more suitable language.

EDIT

The best solution seems to be to save the notebooks as package (.m) files by hand and then translate them using the following code:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冬天的雪花 2024-12-25 02:41:11

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

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 标签(过去的问题)并找到大量元编程和评估控制的示例。

编辑

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

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

您将其用作

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

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

SetAttributes[SymbolName,HoldFirst];
然而,

全局修改内置函数是危险的,因为它可能会对像 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-attributes(请参阅 这个答案)和保留表达式内的替换(请参阅这个问题)。对于手头的情况:

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"}]]

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

You can certainly do this. Here is one way:

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

For example:

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

And importing this as a string:

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]"

However, going to other language to do metaprogramming for Mathematica sounds ridiculous to me, given that Mathematica is very well suited for that. There are many techniques available in Mathematica to do meta-programming and avoid premature evaluation. One that comes to my mind I described in this answer, but there are many others. Since you can operate on parsed code and use the pattern-matching in Mathematica, you save a lot. You can browse the SO Mathematica tags (past questions) and find lots of examples of meta-programming and evaluation control.

EDIT

To ease your pain with auto-evaluating symbols (there are only a few actually, Infinity being one of them).If you just need to get a symbol name for a given symbol, then this function will help:

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

You use it as

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

Alternatively, you can simply add HoldFirst attribute to SymbolName function via SetAttributes. One way is to do that globally:

SetAttributes[SymbolName,HoldFirst];
SymbolName[Infinity]//InputForm

Modifying built-in functions globally is however dangerous since it may have unpredictable effects for such a large system as Mathematica:

ClearAttributes[SymbolName, HoldFirst];

Here is a macro to use that locally:

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

Now,

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

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

You may also wish to do some replacements in a piece of code, say, replace a given symbol by its name. Here is an example code (which I wrap in Hold to prevent it from evaluation):

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

The general way to do replacements in such cases is using Hold-attributes (see this answer) and replacements inside held expressions (see this question). For the case at hand:

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

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

, although this is not the only way to do this. Instead of using the above macro, we can also encode the modification to SymbolName into the rule itself (here I am using a more wordy form ( Trott - Strzebonski trick) of in-place evaluation, but you can use RuleCondition as well:

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

Now, for example:

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

Actually, this entire answer is a good demonstration of various meta-programming techniques. From my own experiences, I can direct you to this, this, this, this and this answers of mine, where meta-programming was essential to solve problem I was addressing. You can also judge by the fraction of functions in Mathematica carrying Hold-attributes to all functions - it is about 10-15 percents if memory serves me well. All those functions are effectively macros, operating on code. To me, this is a very indicative fact, telling me that Mathematica jeavily builds on its meta-programming facilities.

相思故 2024-12-25 02:41:11

表达式的完整形式可以从笔记本的 CodeInput 单元中提取,如下所示:

$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] &

The full forms of expressions can be extracted from the Code and Input cells of a notebook as follows:

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

$exprs is assigned the expressions read, wrapped in Hold to prevent evaluation. $exprs could then be saved into a text file:

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

Package files (.m) are slightly easier to read in this way:

Import["mypackage.m", "HeldExpressions"] //
Flatten[HoldComplete @@ #, 1, HoldComplete] &
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文