如何动态生成 mathematica 代码? [英] How to dynamically generate mathematica code?

查看:28
本文介绍了如何动态生成 mathematica 代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用 mma 制作一种迷你编程语言.从文本文件到包中的模块.理想情况下,我应该能够通过另一个包中的函数从 Mathematica 中生成包和模块.

I want to make a mini programming language in mma. From a textfile to Module(s) in a Package. Ideally I should be able to generate the package and modules from within Mathematica by functions in another package.

问题:这可能吗?我正在寻找参考资料或示例来开始这项工作.

Question: Is this possible? I am looking for a reference or an example to get this started.

例如:

想象一个有 n 个整数类型寄存器的存储库.

Imagine a memory bank with n integer typed registers.

说明是:

1 Z(n)

2 C(m,n)

3 J(m,n,q)

4 S(n)

每一行都有一个地址.第一行 1,第二行 2,以此类推.Z(n) 在寄存器 n 中存储 0.C(m,n) 将寄存器 m 的值存储在寄存器 n 中.J(m,n,q) 如果值寄存器 m 等于寄存器 n 的值,则跳转到地址 q 的行.S(n) 将寄存器 n 中的值加 1.

Each line has an address. First line 1, second 2, etc. Z(n) store 0 in register n. C(m,n) store value of register m in register n. J(m,n,q) if value register m equals value of register n then jump to line with address q. S(n) add 1 to value in register n.

然后给定两个工作程序 P 和 Q 我想生成连接的程序 P+Q.

Then given two working programs P and Q I want to generate the concatenated program P+Q.

然后给定两个工作程序 P 和 Q 我想在 P 之后生成替换 Q.

Then given two working programs P and Q I want to generate the substitution Q after P.

最后,我想开始试验递归......这个迷你项目"的目的.

Finally I want to start experimenting with recursion... the purpose of this 'mini-project'.

推荐答案

您的问题有几个部分.首先,如果你想对你的语言使用一些非 mma 语法,你需要从你的语言到 mma 表达式(你的代码的 AST)制作一个解析器.我将忽略这个(因为这是一个单独的主题)并假设您愿意使用 mma 语法或有办法将您的程序转换为某种 mma 表达式.

Your question has several parts. First, if you want to use some non-mma syntax for your language, you need to make a parser from your language to mma expression (AST of your code). I will leave this one out (since this is a separate topic) and assume that you are willing to use mma syntax or have means to transfer your program to some mma expression.

关于 mma 代码生成,Mathematica 非常适合它,因为它包含代码即数据范式.这里最难的部分是评估控制——我们希望确保我们生成的代码片段在代码生成过程中没有被评估.可以成功地使用评估控制的标准技术,但这通常会使事情变得相当复杂.我将说明一种 mma 代码生成技术,这不是最好/最强大的技术,但最简单.

Regarding the mma code generation, Mathematica is very well suited for it since it embraces the code-is-data paradigm. The hardest part here is the evaluation control - we want to make sure that none of our generated code pieces evaluates during the code-generation process. The standard techniques of evaluation control can be successfully used for that, but this will generally make things rather complicated. I will illustrate one technique of mma code generation, which is not the best/most powerful one, but the easiest.

考虑由这些定义创建的玩具语言:

Consider a toy language created by these definitions:

SetAttributes[testSet, HoldFirst];
SetAttributes[testIf, HoldRest];
SetAttributes[testVar, HoldAll];
SetAttributes[module, HoldAll];
SetAttributes[{package, inContext}, HoldRest];
testPlus[x_, y_] := Plus[x, y];
testTimes[x_, y_] := Times[x, y];
testDivide[x_, y_] := If[y == 0, Inf, Times[x, Power[y, -1]]];
testPower[x_, y_] := If[x == 0 && y < 0, Inf, Power[x, y]];
testSet[HoldPattern[testVar[x_]], expr_] := Set[x, expr];
testVar[x_] := If[ValueQ[x], x, Throw[$Failed, {"varundef", x}]];
testIf[cond_, expr_] := If[cond, expr];
testIf[cond_, expr_, else_] := If[cond, expr, else];
module[{vars__}, body_] := Module[{vars}, body];
package[name_, code_] := (BeginPackage[name]; code; EndPackage[]);
inContext[name_, code_] := (Begin[name]; code; End[]);

以下是这种新语言的一小段代码(包含在 Hold 中):

Here is a small code snippet in this new language (wrapped in Hold):

cd = 
Hold[module[{a}, testSet[testVar[a],
  testPlus[testTimes[testTimes[testPlus[1, 2],
    testPower[testPlus[3, 4], -1]], testPlus[5, 6]], -7]]; testVar[a]]]

对应这个mma代码:

Module[{a},a = (1 + 2)/(3 + 4)*(5 + 6) - 7; a]

我们的代码生成器基于一个非常简单的想法 - 我们将重复将本地规则应用于我们持有的代码.本地规则将从我们函数的定义中提取,如下所示:

Our code-generator is based on a very simple idea - we will repeatedly apply local rules to our held code. The local rules will be extracted from the definitions of our functions, like so:

ClearAll[expansionRules];
expansionRules[heads : {__Symbol}] := Flatten[DownValues /@ heads]

我们需要为我们的语言提供一个头部列表.我将手动执行此操作,但通过创建自定义赋值运算符可以轻松实现自动化.

We need to supply a list of heads for our language. I will do that manually, but it is easy to automate, by creating custom assignment operators.

allHeadsToExpand[] := {testIf, testVar, testPlus, testTimes, testDivide, 
      testPower, testSet, testIf,module,package, inContext}

现在,我们生成我们的代码:

Now, we generate our code:

In[195]:= expanded = cd//.expansionRules[allHeadsToExpand[]]

Out[195]= 
 Hold[Module[{a}, 
    a = ((1 + 2) If[3 + 4 == 0 && -1 < 0, Inf, 1/(3 + 4)]) (5 + 6) - 7; 
    If[ValueQ[a], a, Throw[$Failed, {"varundef", a}]]]]

要执行它,你可以简单地使用 ReleaseHold:

To execute it, you can simply use ReleaseHold:

In[197]:= ReleaseHold[expanded]

Out[197]= -(16/7)

我们构造的优点是我们也可以直接执行我们的 AST:

The advantage of our construction is that we can also execute our AST directly:

In[198]:= ReleaseHold[cd]

Out[198]= -(16/7)

要将其保存到包中,您只需使用 Put 命令即可.以您想要的任何方式扩展语言也很容易.当然,这种语言的代码看起来并不漂亮,因为它本质上是用 mma 表达式表示的 AST.为了使它更漂亮,您需要引入自己的语法并将其编写为 mma AST 的解析器,但那是另一回事了.

To save this to a package, you can simply use Put command. It is also easy to extend the language in any way you want. Of course, the way the code in this language looks is not pretty, since it is essentially the AST expressed as mma expression. To make it prettier, you'd need to introduce your own syntax and write a parser from it to mma AST, but that is another story.

编辑

关于代码生成的自动化并将生成的代码保存到包中:这里有几个实用程序可以做到这一点.

Regarding automating of code-generation and saving the generated code into a package: here are a couple of utilities to do that.

Clear[generateCode];
generateCode[code_Hold] :=
  code //. expansionRules[allHeadsToExpand[]] //.
   HoldPattern[
      CompoundExpression[left___, CompoundExpression[middle___], right___]] :> 
       (left; middle; right);

Clear[formatCode];
formatCode[code_Hold] :=
  StringReplace[Function[Null, ToString[Unevaluated[#], InputForm], HoldAll] @@ 
     code, ";" :> ";\n"];

Clear[saveCode];
saveCode[file_, generatedCode_] :=
 With[{result = BinaryWrite[file, formatCode@generatedCode]},
   Close[file];
   result];

这是相同的示例,但放在一个包中:

Here is the same example but placed in a package:

cdp = Hold[
   package["myPackage`",
     inContext["`Private`",
       module[{a}, 
         testSet[testVar[a],
           testPlus[testTimes[testTimes[testPlus[1, 2],
            testPower[testPlus[3, 4], -1]], testPlus[5, 6]], -7]]; 
         testVar[a]]]]]

我们生成并保存代码如下:

We generate and save the code as follows:

In[101]:= file = FileNameJoin[{"C:","Temp","myPackage.m"}]
Out[101]= C:\Temp\myPackage.m

In[106]:= saved =saveCode[file,generateCode[cdp]]
Out[106]= C:\Temp\myPackage.m

我们可以导入来测试:

In[107]:= Import[file,"Text"]

Out[107]= 
BeginPackage["myPackage`"];
 Begin["`Private`"];
 Module[{a}, a = ((1 + 2)*If[3 + 4 == 0 && -1 < 0, Inf, (3 + 4)^(-1)])*(5 + 6) - 7;
  If[ValueQ[a], a, Throw[$Failed, {"varundef", a}]]];
 End[];
 EndPackage[]

编辑 2

关于您语言中的代码的外观,您可以通过使用 Notation 包来改变输入代码和格式code>/FormatValues 来控制前端呈现的方式.

Regarding the way the code in your language will look, you can make this prettier without going all the way to create your own parser, by using the Notation package to alter the way you can input code and Format/FormatValues to control how it is rendered by the FrontEnd.

这篇关于如何动态生成 mathematica 代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆