gpt4 book ai didi

tree - 通过 Mathematica 的交互式树进行代码操作

转载 作者:行者123 更新时间:2023-12-04 05:57:49 28 4
gpt4 key购买 nike

This question让我开始思考编辑代码的交互式方法。考虑到 Mathematica 的动态功能,我想知道是否有可能实现这样的东西。

考虑一个表达式:

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

及其 TreeForm :

enter image description here

我希望能够直接编辑该树,然后将结果翻译回 Mathematica 代码。一个人至少应该能够:
  • 重命名节点,替换符号
  • 删除节点,将它们的叶子恢复到上面的节点
  • 重新排序节点和叶子(参数的顺序)

  • 我相信有专门从事这种操作的语言或环境,我觉得没有吸引力,但我对这种用于特殊目的的交互式树编辑很感兴趣。

    最佳答案

    我将提供部分解决方案,但可以帮助您入门。我将使用 this 中的可变树数据结构发布,因为对于这个问题来说,可变性似乎是很自然的。为方便起见,在此重复:

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

    下面是从任何 Mathematica 表达式创建可变树并从树中读回表达式的代码:
    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];

    您可以测试简单的表达式,如 a+b .关于其工作原理的一些评论:要从表达式创建可变表达式树(由 node -s 构建),我们调用 makeExpressionTree函数,它首先创建树(调用 makeExpressionTreeAux),然后 inode (调用 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函数遍历树并构建树存储的表达式。

    下面是渲染可变树的代码:
    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], #] &)]];

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

    以下是如何使用它:首先创建一棵树:
    root  = makeExpressionTree[(b + c)*d];

    然后渲染它:
    renderTree[root]

    您必须能够修改每个输入字段中的数据,尽管需要单击几下才能使光标出现在那里。例如,我编辑了 c成为 c1b成为 b1 .然后,你得到修改后的表达式:
    In[102]:= expressionFromTree[root]

    Out[102]= (b1 + c1) d

    此解决方案仅处理修改,但不处理节点等的删除。但是,它可以作为起点,并且也可以扩展到涵盖该起点。

    编辑

    这是一个更短的函数,基于相同的想法,但不使用可变树数据结构。
    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, #] &)]
    ]

    以下是你如何使用它:
    renderTreeAlt[(a + b) c + d]

    您可以调用 getExpression[]随时查看表达式的当前值或将其分配给任何变量,或者您可以使用
    Dynamic[getExpression[]]

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

    关于tree - 通过 Mathematica 的交互式树进行代码操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6138540/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com