通过 Mathematica 的交互式树进行代码操作 [英] code manipulation via interactive tree for Mathematica

查看:18
本文介绍了通过 Mathematica 的交互式树进行代码操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

帖子中的可变树数据结构,因为它对于这个问题,可变性似乎是很自然的.为方便起见在此重复:

模块[{parent, children, value},儿童 [_] := {};值[_]:=空;节点/:新[节点[]]:=节点[唯一[]];节点/:节点[tag_].getChildren[] := children[tag];节点/:节点[tag_].addChild[child_node, index_] :=儿童[标签] = 插入[儿童[标签],儿童,索引];节点/: 节点 [tag_].removeChild[child_node, index_] :=儿童[标签] = 删除[儿童[标签],索引];节点/:节点[tag_].getChild[index_] := children[tag][[index]];节点/:节点[标签_].getValue[]:=值[标签];节点/:节点[tag_].setValue[val_] := value[tag] = val;];

以下是从任何 Mathematica 表达式创建可变树的代码,并从树中读取表达式:

清除[makeExpressionTreeAux];makeExpressionTreeAux[expr_?AtomQ] :=随着[{nd = new[node[]], val = Hold[Evaluate[Unique[]]]},nd.setValue[val];评估[val[[1]]] = expr;nd];makeExpressionTreeAux[expr_] :=随着[{nd = new[node[]], val = Hold[Evaluate[Unique[]]]},nd.setValue[val];评估[val[[1]]] = Head[expr];Do[nd.addChild[makeExpressionTreeAux[expr[[i]]], i], {i, Length[expr]}];nd];清除[表达式从树];表达式FromTree[nd_node]/;nd.getChildren[] == {} := (nd.getValue[])[[-1, 1]];表达式FromTree[nd_node] :=Apply[(nd.getValue[])[[-1, 1]], Map[expressionFromTree, nd.getChildren[]]];清除[遍历];遍历[root_node, f_] :=模块[{},f[根];Scan[traverse[#, f] &, root.getChildren[]]];清除[索引节点];indexNodes[root_node] :=模块[{i = 0},遍历[root, #.setValue[{i++, #.getValue[]}] &]];清除[makeExpressionTree];makeExpressionTree[expr_] :=随着[{root = makeExpressionTreeAux[expr]},indexNodes[root];根];

您可以测试像 a+b 这样的简单表达式.关于其工作原理的一些评论:要从表达式创建可变表达式树(由 node-s 构建),我们调用 makeExpressionTree 函数,该函数首先创建树(调用makeExpressionTreeAux),然后索引节点(调用indexNodes).makeExpressionTree 函数是递归的,它递归遍历表达式树,同时将其结构复制到结果可变树的结构中.这里的一个微妙点是为什么我们需要像 val = Hold[Evaluate[Unique[]]], nd.setValue[val];, Evaluate[val] 之类的东西[[1]]] = expr; 而不仅仅是 nd.setValue[expr].这是通过 InputField[Dynamic[some-var]] 完成的 - 为此,我们需要一个变量来存储值(也许,可以编写一个更自定义的 Dynamic> 来避免这个问题,如果喜欢的话).因此,在创建树之后,每个节点都包含一个值 Hold[someSymbol],而 someSymbol 包含原子或头部的值,对于非-原子子部分.索引过程将每个节点的值从 Hold[sym] 更改为 {index,Hold[symbol]}.请注意,它使用了 traverse 函数,该函数实现了通用的深度优先可变树遍历(类似于 Map[f,expr, Infinity],但适用于可变树).因此,索引以深度优先的顺序递增.最后,expressionFromTree 函数遍历树并构建树存储的表达式.

这是渲染可变树的代码:

清除[getGraphRules];getGraphRules[root_node] :=压平[地图[线程,规则 @@@收割[遍历[根,播种[{First[#.getValue[]],Map[First[#.getValue[]] &, #.getChildren[]]}] &]][[2, 1]]]]清除[getNodeIndexRules];getNodeIndexRules[root_node] :=Dispatch@Reap[traverse[root, Sow[First[#.getValue[]]] ->#] &]][[2, 1]];清除[makeSymbolRule];makeSymbolRule[nd_node] :=随着[{val = nd.getValue[]},RuleDelayed @@ Prepend[Last[val], First[val]]];清除[渲染树];渲染树[root_node] :=随着[{grules = getGraphRules[root],ndrules = getNodeIndexRules[root]},TreePlot[grules, VertexRenderingFunction ->(插入[InputField[动态[#2],FieldSize ->10]/.makeSymbolRule[#2/.规则], #] &)]];

这部分的工作原理如下:getGraphRules 函数遍历树并收集节点索引的父子对(以规则的形式),结果规则集就是 GraphPlot 期望作为第一个参数.getNodeIndexRules 函数遍历树并构建哈希表,其中键是节点索引,值是节点本身.makeSymbolRule 函数获取节点并返回 index:>node-var-symbol 形式的延迟规则.延迟规则很重要,这样符号就不会求值.这用于将符号从节点树插入InputField[Dynamic[]].

以下是如何使用它:首先创建一棵树:

root = makeExpressionTree[(b + c)*d];

然后渲染它:

renderTree[root]

您必须能够修改每个输入字段中的数据,尽管需要点击几下才能使光标出现在那里.例如,我将c 编辑为c1,将b 编辑为b1.然后,你得到修改后的表达式:

In[102]:= expressionFromTree[root]输出[102]= (b1 + c1) d

此解决方案仅处理修改,而不处理节点的删除等.然而,它可以作为一个起点,并扩展到涵盖这一点.

编辑

这是一个更短的函数,基于相同的想法,但不使用可变树数据结构.

清除[renderTreeAlt];renderTreeAlt[expr_] :=模块[{newExpr, indRules, grules, assignments, i = 0, set},getExpression[] := newExpr;newExpr = expr/.x_符号:>set[i++, Unique[], x];格鲁斯 =展平[线程/@规则@@@案例[newExpr, set[i_, __][args___] :>{i, Map[If[MatchQ[#, _set], First[#], First[#[[0]]]] &, {args}]},{0, 无穷大}]];indRules = 调度@Cases[newExpr, set[ind_, sym_, _] :>(ind :> sym), {0, Infinity}, Heads ->真的];作业 =Cases[newExpr, set[_, sym_, val_] :>set[sym , val], {0, Infinity},Heads ->真的];newExpr = newExpr/.设置 [_, sym_, val_] :>符号;作业/.设置 ->放;TreePlot[grules, VertexRenderingFunction ->(插入[InputField[动态[#2],FieldSize ->10]/.indRules, #] &)]]

这是你如何使用它:

renderTreeAlt[(a + b) c + d]

你可以随时调用getExpression[]查看当前表达式的值或者赋值给任意变量,也可以使用

动态[getExpression[]]

这种方法产生了更短的代码,因为 Mathematica 原生树结构被重新用作树的骨架,其中所有信息部分(头部和原子)都被符号替换.只要我们可以访问原始符号而不仅仅是它们的值,这仍然是一个可变树,但是我们不需要考虑构建树的块 - 我们为此使用表达式结构.这并不是要减少之前的较长的解决方案,从概念上我认为它更清晰,并且对于更复杂的任务可能仍然更好.

This question caused me to ponder an interactive method for editing code. I wonder if it is possible to implement something like this given the dynamic capabilities of Mathematica.

Consider an expression:

Text[Row[{PaddedForm[currentTime, {6, 3}, NumberSigns -> {"", ""}, NumberPadding -> {"0", "0"}]}]]

And its TreeForm:

I would like to be able to edit that tree directly, and then have the result translated back into Mathematica code. One should at least be able to:

  • rename nodes, replacing symbols
  • delete nodes, reverting their leaves to the node above
  • reorder nodes and leaves (the order of arguments)

I believe that there are languages or environments which specialize in this kind of manipulation, and I do not find that attractive, but I am interested in having this kind of interactive tree editing for special purposes.

解决方案

I will provide a partial solution, but the one that could get you started. I will use the mutable tree data structure from this post, since it seems like mutability is natural for this problem. Repeating it for convenience here:

Module[{parent, children, value},
  children[_] := {};
  value[_] := Null;
  node /: new[node[]] := node[Unique[]];
  node /: node[tag_].getChildren[] := children[tag];
  node /: node[tag_].addChild[child_node, index_] := 
     children[tag] = Insert[children[tag], child, index];
  node /: node[tag_].removeChild[child_node, index_] := 
     children[tag] = Delete[children[tag], index];
  node /: node[tag_].getChild[index_] := children[tag][[index]];
  node /: node[tag_].getValue[] := value[tag];
  node /: node[tag_].setValue[val_] := value[tag] = val;
];

Here is the code to create a mutable tree from any Mathematica expression, and read the expression back from the tree:

Clear[makeExpressionTreeAux];
makeExpressionTreeAux[expr_?AtomQ] :=
  With[{nd = new[node[]], val = Hold[Evaluate[Unique[]]]},
    nd.setValue[val];
    Evaluate[val[[1]]] = expr;
    nd];
makeExpressionTreeAux[expr_] :=
  With[{nd = new[node[]], val = Hold[Evaluate[Unique[]]]},
   nd.setValue[val];
   Evaluate[val[[1]]] = Head[expr];
   Do[nd.addChild[makeExpressionTreeAux[expr[[i]]], i], {i, Length[expr]}];
   nd];

Clear[expressionFromTree];
expressionFromTree[nd_node] /; nd.getChildren[] == {} := (nd.getValue[])[[-1, 1]];
expressionFromTree[nd_node] := 
  Apply[(nd.getValue[])[[-1, 1]], Map[expressionFromTree, nd.getChildren[]]];

Clear[traverse];
traverse[root_node, f_] :=
  Module[{},
   f[root];
   Scan[traverse[#, f] &, root.getChildren[]]];

Clear[indexNodes];
indexNodes[root_node] :=
  Module[{i = 0},
     traverse[root, #.setValue[{i++, #.getValue[]}] &]];

Clear[makeExpressionTree];
makeExpressionTree[expr_] :=
  With[{root  = makeExpressionTreeAux[expr]},
   indexNodes[root];
   root];

You can test on simple expressions like a+b. A few comments on how this works: to create a mutable expression tree (built of node-s) from an expression, we call the makeExpressionTree function, which first creates the tree (call to makeExpressionTreeAux), and then indexes the nodes (call to indexNodes). The makeExpressionTree function is recursive, it recursively traverses the expression tree while copying its structure to the structure of the resulting mutable tree. One subtle point here is why we need things like val = Hold[Evaluate[Unique[]]], nd.setValue[val];, Evaluate[val[[1]]] = expr; rather than just nd.setValue[expr]. This is done with InputField[Dynamic[some-var]] in mind - for this, we need a variable to store the value (perhaps, one could write a more custom Dynamic to avoid this problem if one likes). So, after the tree is created, each node contains a value that is Hold[someSymbol], while someSymbol contains the value of an atom, or of a head, for non-atomic sub-part. The indexing procedure changes the value of each node from Hold[sym] to {index,Hold[symbol]}. Note that it uses the traverse function which implements the generic depth-first mutable tree traversal (similar to Map[f,expr, Infinity], but for mutable trees). Therefore, indexes are incremented in depth-first order. Finally, the expressionFromTree function traverses the tree and builds the expression that the tree stores.

Here is the code to render the mutable tree:

Clear[getGraphRules];
getGraphRules[root_node] :=
 Flatten[
  Map[Thread,
   Rule @@@ 
     Reap[traverse[root, 
       Sow[{First[#.getValue[]], 
         Map[First[#.getValue[]] &, #.getChildren[]]}] &]][[2, 1]]]]

Clear[getNodeIndexRules];
getNodeIndexRules[root_node] :=
 Dispatch@ Reap[traverse[root, Sow[First[#.getValue[]] -> #] &]][[2, 1]];

Clear[makeSymbolRule];
makeSymbolRule[nd_node] :=
   With[{val = nd.getValue[]},
      RuleDelayed @@ Prepend[Last[val], First[val]]];

Clear[renderTree];
renderTree[root_node] :=
 With[{grules = getGraphRules[root],
    ndrules = getNodeIndexRules[root]},
     TreePlot[grules, VertexRenderingFunction ->
      (Inset[
        InputField[Dynamic[#2], FieldSize -> 10] /. 
          makeSymbolRule[#2 /. ndrules], #] &)]];

This part works as follows: the getGraphRules function traverses the tree and collects parent-child pares of node indices (in the form of rules), the resulting set of rules is what the GraphPlot expects as a first argument. The getNodeIndexRules function traverses the tree and builds the hash table where keys are node indices and values are the nodes themselves. The makeSymbolRule function takes the node and returns the delayed rule of the form index:>node-var-symbol. It is important that the rule is delayed, so that the symbols do not evaluate. This is used to insert the symbol from the node tree into InputField[Dynamic[]].

Here is how you can use it: first create a tree:

root  = makeExpressionTree[(b + c)*d];

Then render it:

renderTree[root]

You must be able to modify data in each input field, although it takes a few clicks to make the cursor appear there. For example, I edited c to be c1 and b to be b1. Then, you get the modified expression:

In[102]:= expressionFromTree[root]

Out[102]= (b1 + c1) d

This solution handles only modifications, but not removal of nodes etc. It can however be a starting point, and be extended to cover that as well.

EDIT

Here is a much shorter function, based on the same ideas but not using the mutable tree data structure.

Clear[renderTreeAlt];
renderTreeAlt[expr_] :=
  Module[{newExpr, indRules, grules, assignments, i = 0, set},
    getExpression[] := newExpr;
    newExpr = expr /. x_Symbol :> set[i++, Unique[], x];
    grules = 
      Flatten[ Thread /@ Rule @@@ 
        Cases[newExpr, set[i_, __][args___] :> 
          {i, Map[If[MatchQ[#, _set], First[#], First[#[[0]]]] &, {args}]}, 
          {0, Infinity}]];
   indRules = Dispatch@ 
        Cases[newExpr, set[ind_, sym_, _] :> (ind :> sym), {0, Infinity}, Heads -> True];
   assignments = 
       Cases[newExpr, set[_, sym_, val_] :> set[sym , val], {0, Infinity},Heads -> True];
   newExpr = newExpr /. set[_, sym_, val_] :> sym;
   assignments /. set -> Set;
   TreePlot[grules, VertexRenderingFunction -> (Inset[
           InputField[Dynamic[#2], FieldSize -> 10] /. indRules, #] &)]
]

Here is how you use it:

renderTreeAlt[(a + b) c + d]

You can call getExpression[] at any time to see the current value of expression or assign it to any variable, or you can use

Dynamic[getExpression[]]

This method yields much shorter code since the Mathematica native tree structure is reused as a skeleton for the tree, where all informative pieces (heads and atoms) were replaces by symbols. This is still a mutable tree as long as we have access to original symbols and not just their values, but we don't need to think about building blocks for the tree - we use expression structure for that. This is not to diminish the previous longer solution, conceptually I think it is more clear, and it is probably still better for more complicated tasks.

这篇关于通过 Mathematica 的交互式树进行代码操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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