我將提供一個部分解決方案,但可以讓你開始。我將使用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;
];
下面是從任何數學表達式創建一個可變的樹,閱讀表達從樹後面的代碼:
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
來避免這個問題)。因此,在創建樹之後,每個節點包含的值爲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] :=
[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
功能遍歷樹並收集親子剝節點指標(以規則的形式),由此產生的一組規則是GraphPlot
預期的第一個參數。 getNodeIndexRules
函數遍歷樹並構建散列表,其中鍵是節點索引,值是節點本身。 makeSymbolRule
函數獲取節點並返回形式爲index:>node-var-symbol
的延遲規則。規則延遲很重要,這樣符號不會評估。這用於將節點樹中的符號插入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原生樹結構被重新用作樹的骨架,其中所有有信息的部分(頭部和原子)被符號替換,所以該方法產生更短的代碼。只要我們能夠訪問原始符號而不僅僅是它們的值,這仍然是一個可變的樹,但我們不需要考慮爲樹構建模塊 - 我們使用表達式結構。這不是爲了減少以前的更長時間的解決方案,在概念上我認爲它更清楚,對於更復雜的任務來說,它可能還是更好。
我認爲自然的方法是使用XXX/Link和類似http://orange.biolab.si/doc/catalog10/Classify/InteractiveTreeBuilder.htm(我的意思是,只是界面,而不是分類部分) – 2011-05-26 14:34:31
你能指導我們這些特殊目的嗎?我很難想象這會如何有用。 – 2011-05-26 14:35:48
@Sjoerd,對不起,我現在忘了回答你了。我沒有任何宏偉計劃,這只是一個可能有用的替代方案。還有其他問題,如MathCAD,SPICE和(我不記得其他問題),它們使用視覺塊組裝範例。對於一般的編程來說這將是乏味的,但它確實有它的位置。 – 2011-05-27 10:29:35