9
votes

Comment construire un et un arbre?

J'ai besoin d'une arborescence qui prend en charge "et" et "ing. Par exemple, étant donné une expression régulière comme AB | C (d | e) Je veux transformer cela dans un arbre.

Donc, au début, nous avons deux "ou" branches ... peut soit descendre AB , ou c (d | e) . Si vous dirigez la branche AB , vous obtenez deux nœuds, A et B (ou a suivi de B , peu importe). Ensuite, si vous descendez la branche c (d | e) , vous obtenez C et (d | e) , alors (d | e) est divisé en d ou e .

Faire une structure d'arbre est facile, vous avez simplement quelque chose comme xxx

mais comment savez-vous si les enfants doivent être "anded" ou "oré"? Je suppose que chaque niveau de l'arbre devrait alterner entre "anding" et "oring"

c'est logique? Quelqu'un peut-il suggérer une structure pour cela?


Quelques personnes ont suggéré de stocker l'opérateur sur le nœud, qui va bien, mais ce n'est pas un moyen de tirer parti du fait que Chaque niveau alterne toujours ou, et, et, et, et ...?

EDIT: Pas tout à fait sûr pourquoi les gens continuent à supposer qu'il s'agisse d'un arbre binaire. ce n'est pas . J'espérais que le petit extrait de code vous inclinait. L'exemple juste arrive n'a que 2 branches.


actuellement penché vers cette: xxx


15 commentaires

Je pense que l'alternance et / ou les niveaux est juste une coïncidence pour cet exemple. En général, vous devriez être capable de mélanger et et au même niveau. Voir ma réponse.


@mbeckish: Est-ce? Pouvez-vous fournir un exemple où vous ne serait pas alterner?


De plus, vous n'avez pas besoin d'un éventail d'enfants (sauf si vous allez soutenir les opérateurs N-ARY).


@RRALPH: A | B | C est un exemple trivial.


@MBeckish: Non ... c'est juste une couche avec 3 branches. Personne n'a dit que c'était un arbre binaire. C'est pourquoi j'ai utilisé un tableau dans mon exemple.


@RRALPH - Si vous souhaitez effondrer plusieurs opérateurs binaires dans un seul nœud N-Ary, vous pourrez peut-être assumer des couches alternées.


@RRALPH - En ce qui concerne votre édition, je pense que beaucoup de gens le modéliseraient comme un arbre binaire, car les opérateurs sont binaires (ou unis si vous soutenez pas)!


@MBeckish: la première phrase lit "Compte tenu de l'expression régulière". Cela indique que j'utilise des opérateurs de regex, pas d'autre type binaire.


@Alph - n'a pas analysé ce petit texte. De plus, le titre dit "et ou arbre". Peut-être que vous pouvez le changer pour le rendre plus évident que vous souhaitez analyser les expressions générales régulières dans un arbre (que je pense ajouterait beaucoup plus de complications).


@RRALPH - Plus, "AB" n'est pas "A et B" dans une expression régulière - c'est "A suivi de B". Maintenant, je suis vraiment confondu de ce que vous demandez.


@MBeckish: Je ne voulais pas mettre aussi beaucoup d'accentues sur cela ... J'ai déjà réussi à analyser le rep sans utiliser d'arbres ... Eh bien, un arbre de 2 niveaux + récursion, Mais je pense avoir besoin d'une nouvelle structure pour ajouter d'autres fonctionnalités. Je n'ai besoin que de soutien pour les 2 opérateurs (je pense).


Comment vous faites (A | B | C) (DE) avec chaque niveau d'arbre et ou ou seulement?


@ Hpt: vous mettez d , E et un "ou" nœud de la même ligne (et-d ensemble), puis a , B , C sur la ligne suivante (OR). Cela fonctionne, mais vous avez laissé entendre un autre problème que nous avons perdu les informations de regroupement autour de (de) (comme si les crochets ne sont pas là ... ce qui est parfaitement bien jusqu'à ce que vous ayez faire des références de retour)


@RRALPH: Concernant votre dernier commentaire: Vous jouez maintenant avec l'entrée plutôt que de l'analyser. Si vous souhaitez réduire la chaîne initiale avant de l'analyser, c'est une option, mais c'est mauvais. PS - De quelle classe s'agit-il?


@Norla: Que veux-tu dire? J'allais analyser la corde dans un arbre, puis l'évaluer. Pourquoi est-ce une mauvaise approche? Voilà comment les analyseurs sont écrits, apparemment. Ceci est tout pour Stackoverflow.com/questions/4298541


7 Réponses :


5
votes
abstract class Node { }

class DataNode : Node {
    public string Data { get; }

    // details
}

class OperatorNode : Node {
    public Node Left { get; }
    public Node Right { get; }
    public BinaryOperator Operator { get; }

    // details
}

abstract class BinaryOperator { // details }

class Or : BinaryOperator { // details }
class And : BinaryOperator { // details }

2 commentaires

Alors, comment l'exemple d'AB | C (D | E) serait-il mis en œuvre?


Notez également que ce n'est pas un arbre binaire, mais c'est une solution facile. Et où sont les "données" stockées? Seules les feuilles ont des données ... juste le mettre dans le nœud et le laisser comme null à moins que sa feuille?



4
votes

Y a-t-il quelque chose de mal avec ceci:

Node root = new Node
        {
            operation = Operation.Or,
            children = new Node[]
            {
                new Node
                {
                    operation = Operation.And,
                    children = new Node[]
                    {
                          new Node{ element = "a" },
                          new Node{ element="b" }
                    }
                },
                new Node
                {
                    children = new Node[]
                    {
                        new Node{ element = "c"},
                        new Node
                        {
                            operation= Operation.Or,
                            children = new Node[]
                            {
                                new Node{ element= "d"},
                                new Node{element = "e"}
                            }
                        }
                    }
                }
            }
        };


2 commentaires

Comment l'exemple AB | C (D | E) serait-il mis en œuvre?


@MBeckish: J'aime mieux votre approche, séparant mon numéro type en deux types (et chaque nouveau noeud serait remplacé par nouvel opérateurNode ou nouvel élémentnode



2
votes

Je l'ai fait il y a quelques jours à l'aide de antlr . Antlr m'a fourni une grammaire qui est représentée comme une arborescence de syntaxe AST abstraite que vous venez de décrire et généré C # code pouvant gérer cette grammaire.

C'est assez agréable et élégant. Voici un quelques exemple .


0 commentaires

5
votes

Vous pouvez avoir 2 types de nœuds: nœuds de l'opérateur et nœuds variables.

Les feuilles de votre arbre seront tous des nœuds variables; Tous les autres nœuds seraient des nœuds de l'opérateur. P>

Les nœuds d'opérateur binaires auraient deux enfants. Opérateur unaire (comme non) Les nœuds auraient 1 enfant. P>

pour votre exemple AB | C (D | E): P>

      OR
  /         \
 AND       AND
 / \      /  \
a   b    c   OR
           /  \
          d    e


1 commentaires

Programmatiquement cela semble tout droit à mettre en œuvre. L'analyse impliquerait le démarrage et la plus grande feuille et les nœuds traversants.



1
votes

Juste pour lancer un peu différent

interface Node
{
    // top level operations here
}

class OpNode : Node
{
    public Node Left { get; set; }
    public Node Right { get; set; }
}

class AndNode : OpNode
{
    public AndNode(Node left, Node right)
    {
        Left = left;
        Right = right;
    }
    public override string ToString()
    {
        return "(" + Left.ToString() + " & " + Right.ToString() + ")";
    }
}

class OrNode : OpNode
{
    public OrNode(Node left, Node right)
    {
        Left = left;
        Right = right;
    }
    public override string ToString()
    {
        return "(" + Left.ToString() + " | " + Right.ToString() + ")";
    }
}

class DataNode<T> : Node
{
    T _data;
    public DataNode(T data)
    {
        _data = data;
    }
    public override string ToString()
    {
        return _data.ToString();
    }
}


1 commentaires

Cela ressemble à la solution de Mbeckish, mais vous avez également pris une étape plus loin et divisez les nœuds OP dans deux types différents.



1
votes

Que diriez-vous de quelque chose de ce simple: xxx

chaque classe pourrait avoir son propre évaluer () qui serait et ou tous les enfants comme si nécessaire

Vous voudrez peut-être toujours avoir une superclasse parent afin que votre code puisse contenir des nœuds génériques sans se soucier de savoir si le premier était et ou ou.


1 commentaires

C'est la seule solution jusqu'à présent qui force une alternance ... et c'est simple. Je l'aime bien.



13
votes

Pensez à une structure d'arborescence où chaque nœud représente une expression booléenne qui peut être évaluée pour être véridique ou fausse - dans votre cas une expression régulière (correspondance ou non-match). La structure des arbres elle-même représente et et ou: Chaque route, en commençant par le nœud racine et se terminant par un nœud qui n'a aucun autre enfant, est une conjonction d'expressions, représentant et. L'arborescence xxx pré>

représenterait A et B et c. P>

Chaque fois qu'un nœud a plus d'un nœud enfant, il y a une ou (disjonction), ramifiant dans plusieurs routes : p> xxx pré>

représente a et ((b et c) ou d) p>

de sorte que vous ne devez même pas stocker les opérateurs nulle part. p >

Dans votre exemple, vous avez l'expression ab | c (d | e) code> donc il n'y a pas d'expression racine commune à évaluer; Je suggère que la racine dans ce cas est simplement true code> et l'arbre ressemblerait à: p>

   true
   / \
  A   C
 /   / \
B   D   E


1 commentaires

J'aime ça!! Il supprime la nécessité de garder une trace de l'alternance ou des opérateurs, et sa lecture propre et facile à lire.