2011-05-26 16 views
8

This question için etkileşimli ağaç üzerinden kod manipülasyonu, kodu düzenlemek için etkileşimli bir yöntem düşünmemize neden oldu. Mathematica'nın dinamik yetenekleri göz önünde bulundurulduğunda böyle bir şeyin uygulanmasının mümkün olup olmadığını merak ediyorum.Mathematica

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

Ve onun TreeForm:

enter image description here

doğrudan O ağacı düzenleyebilmek ister ve ardından Mathematica koduna geri çevrilmiş sonuca ulaşılır

bir ifade düşünün . ,

  • adlandırma düğümleri
  • düğümleri silmek yerine simgeler
  • yeniden sipariş düğümleri ve yaprak (bağımsız değişkenler sırası)
üzerinde düğüme yapraklarını geri alınmasının: bir en azından mümkün olmalıdır

Bu tür bir manipülasyonda uzmanlaşmış dillerin veya ortamların olduğuna inanıyorum ve bu kadar çekici bulmuyorum, ancak özel amaçlı bu tür bir etkileşimli ağaç düzenlemesine sahip olmakla ilgileniyorum.

+0

Doğal yolun XXX/Link'i ve bunun gibi bir şeyi kullanması olduğunu düşünüyorum. Http://orange.biolab.si/doc/catalog10/Classify/InteractiveTreeBuilder.htm (Yani, sadece arabirim değil, sınıflandırma kısmı) –

+0

Bize bu özel amaçları aydınlatır mısınız? Bunun nasıl faydalı olabileceğini hayal etmekte zorlanıyorum. –

+0

@Sjoerd, özür dilerim, size şimdiden cevap vermeyi unuttum. Hiç büyük planım yok, sadece zaman zaman yararlı olabilecek bir alternatif. MathCAD, SPICE ve (diğerlerini hatırlayamıyorum), görsel blok montaj paradigması kullanan başka problemler var. Genel programlama için sıkıcı olurdu, ama yeri var. –

cevap

13

Kısmi bir çözüm sunacağım, ancak başlamanıza neden olabilecek olanı. Bu sorun için doğal mutabilite gibi göründüğünden, değişebilir ağaç veri yapısını this gönderiminden kullanacağım. Burada kolaylık için tekrarlanması:

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

Sen gibi basit ifadeleriyle ilgili test edebilirsiniz: İşte

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

geri ağacından ifadesini herhangi Mathematica ifadeden bir değişken ağacı oluşturmak ve okumak için kod edilir a+b. Bunun nasıl çalıştığına dair birkaç yorum: bir ifadeden bir değişken ifade ağacı oluşturmak için ( node -s) makeExpressionTree işlevini çağırırız; ilk önce ağacı oluşturur ( makeExpressionTreeAux çağrısı) ve sonra düğümleri dizinler. indexNodes). makeExpressionTree işlevi, yinelemeli olup, yapısını sonuçta ortaya çıkan değişebilir ağacın yapısına kopyalarken, ifade ağacını yinelemeli olarak çaprazlar. Burada bir ince nokta, sadece nd.setValue[expr] yerine val = Hold[Evaluate[Unique[]]], nd.setValue[val];, Evaluate[val[[1]]] = expr; gibi şeylere ihtiyacımızın nedeni. Bu akılda InputField[Dynamic[some-var]] ile yapılır - bunun için, değeri saklamak için bir değişkene ihtiyacımız var (belki de bir tane daha hoşlanırsa bu sorunu önlemek için daha özel bir Dynamic yazabiliriz). Bu nedenle, ağaç oluşturulduktan sonra, her düğüm Hold[someSymbol], someSymbol ise atom olmayan alt bölüm için bir atomun veya bir başlığın değerini içerir. Endeksleme prosedürü, her bir düğümün değerini Hold[sym]'dan {index,Hold[symbol]}'a değiştirir. Genel derinlik-ilk değişken ağaç geçişini uygulayan traverse işlevini kullandığını unutmayın ( Map[f,expr, Infinity]'a benzer, ancak değiştirilebilir ağaçlar için). Bu nedenle, indeksler derinlik-birinci sırada artırılır. Son olarak, expressionFromTree işlevi, ağacı çaprazlar ve ağacın depoladığı ifadeyi oluşturur.

Burada değişken ağaç işlemek için kodudur:

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], #] &)]]; 

şöyle bu bölümü çalışır: getGraphRules fonksiyon ağaç boyunca ilerleyen ve (kurallar şeklinde) ebeveyn-çocuk düğüm endekslerinin pares toplar, Ortaya çıkan kurallar, ilk argüman olarak GraphPlot'un ne beklediğidir. getNodeIndexRules işlevi, ağacı geçirir ve anahtarlar düğümü ve değerlerin düğümlerin kendileri olduğu karma tabloyu oluşturur. makeSymbolRule işlevi, düğümü alır ve index:>node-var-symbol formunun gecikmeli kuralını döndürür. Kuralın ertelenmesi önemlidir, böylece semboller değerlendirilmez. Bu, düğümü ağaçtan InputField[Dynamic[]]'a eklemek için kullanılır. İlk ağacı oluşturmak:

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

Sonra işlemek: Burada

kullanmadan nasıl

renderTree[root] 

Tek gereken karşın, her giriş alanına verileri değiştirmek gerekir İmlecin orada görünmesi için birkaç tıklama. Örneğin, c1 ve b olmak üzere c'u b1 olarak düzenledim. Daha sonra, değiştirilmiş ifade almak:

In[102]:= expressionFromTree[root] 

Out[102]= (b1 + c1) d 

Bu çözelti Bununla birlikte, bir başlangıç ​​noktası olabilir, ve aynı zamanda bu kapsayacak şekilde genişletilebilir tek değişiklik, ancak düğüm vb kaldırılmasını ele alır.

DÜZENLEME İşte

aynı fikirleri ancak değişken ağaç veri yapısını kullanmayan tabanlı çok daha kısa bir fonksiyonudur. İşte

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, #] &)] 
] 

Kullanmaya nasıl:

renderTreeAlt[(a + b) c + d] 

Sen ifadenin geçerli değerini görmek veya herhangi bir değişkene atamak için herhangi bir zamanda getExpression[] arayabilir, ya da kullanabileceğiniz

Dynamic[getExpression[]] 

Bu yöntem, Mathematica yerel ağaç yapısı ağaç için bir iskelet olarak yeniden kullanıldığından, tüm bilgilendirici parçaların (başlıklar ve atomlar) sembollerle yer değiştirdiği için çok daha kısa kodlar sağlar. Bu, orijinal sembollere ve sadece onların değerlerine erişemediğimiz sürece hala değişmez bir ağaçtır, ancak ağaç için yapı taşlarını düşünmemize gerek yoktur - bunun için ifade yapısını kullanırız. Bu, önceki daha uzun çözümü azaltmak değil, kavramsal olarak daha net olduğunu düşünüyorum ve muhtemelen daha karmaşık görevler için daha iyi.

+0

Ne yazık ki, bunun nasıl çalıştığını açıklamak için zamanım yok, ama zaman izin verdiği anda bunu yapacağım. –

+0

iyi, yaprakları değiştirmek için söz verdiği gibi çalışır. Kimsenin bu kadar ilgi çekici olduğunu düşünmedim ayıp ... – acl

+1

@acl Belki de, çoğu insan için noktayı hızlı bir şekilde görmek için çok fazla kod. Benim için bu ilginç bir egzersizdi. Ayrıca, ifadeleri değişken ağaçlara ve arkaya dönüştürme kodu oldukça geneldir ve belki başka kullanımlar da bulabilir. Her neyse, teşekkürler için teşekkürler! –

İlgili konular