Ответ 1
Я предоставлю частичное решение, но это поможет вам начать. Я буду использовать измененную структуру данных дерева из этой почты, так как это кажется изменчивостью для этой проблемы. Повторите это для удобства здесь:
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
), а затем индексирует узлы (вызов indexNodes
). Функция makeExpressionTree
рекурсивна, она рекурсивно обходит дерево выражений при копировании его структуры в структуру полученного изменяемого дерева. Один из тонких моментов заключается в том, почему нам нужны такие вещи, как val = Hold[Evaluate[Unique[]]]
, nd.setValue[val];
, Evaluate[val[[1]]] = expr;
, а не только nd.setValue[expr]
. Это делается с учетом InputField[Dynamic[some-var]]
- для этого нам нужна переменная для хранения значения (возможно, можно написать более пользовательский Dynamic
, чтобы избежать этой проблемы, если вам нравится). Таким образом, после создания дерева каждый node содержит значение Hold[someSymbol]
, а someSymbol
содержит значение атома или головы для неатомной подчасти. Процедура индексации изменяет значение каждого node от 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] :=
[email protected] 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
обходит дерево и собирает родительские-родительские пары индексов node (в форме правил), результирующий набор правил - это то, что ожидает GraphPlot
как первый аргумент. Функция getNodeIndexRules
обходит дерево и строит хеш-таблицу, где ключи node индексы и значения являются самими узлами. Функция makeSymbolRule
принимает node и возвращает правило с задержкой формы index:>node-var-symbol
. Важно, чтобы правило было отложено, чтобы символы не оценивались. Это используется для вставки символа из дерева node в InputField[Dynamic[]]
.
Вот как вы можете его использовать: сначала создайте дерево:
root = makeExpressionTree[(b + c)*d];
Затем выполните его:
renderTree[root]
Вы должны иметь возможность изменять данные в каждом поле ввода, хотя для отображения курсора требуется несколько щелчков мыши. Например, я редактировал c
как c1
и b
как 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 = [email protected]
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 повторно используется в качестве скелета для дерева, где все информационные части (головки и атомы) заменяются символами. Это все еще мутируемое дерево, если у нас есть доступ к оригинальным символам, а не только к их значениям, но нам не нужно думать о создании блоков для дерева. Для этого мы используем структуру выражений. Это не должно уменьшать предыдущее более длинное решение, концептуально, я думаю, что это более понятно, и, вероятно, это еще лучше для более сложных задач.