Question

Bonjour, je suis en train de refactoriser un de mes programmes et j'ai trouvé un problème intéressant.

J'ai des transitions dans un automate. Les transitions ont toujours un état de début et un état de fin. Certaines transitions ont une étiquette qui code une certaine action qui doit être exécutée lors du parcours. Pas d'étiquette signifie pas d'action. Certaines transitions ont une condition qui doit être remplie pour pouvoir traverser cette condition. S'il n'y a pas de condition, la transition est fondamentalement une transition epsilon dans un NFA et sera traversée sans consommer de symbole d'entrée.

J'ai besoin des opérations suivantes:

  • vérifier si la transition a une étiquette
  • obtenir cette étiquette
  • ajouter une étiquette à une transition
  • vérifier si la transition a une condition
  • obtenir cette condition
  • vérifier l’égalité

À en juger par les cinq premiers points, cela ressemble à un décorateur clair, avec une transition de base et deux décorateurs: Labeled et Condition. Cependant, cette approche pose un problème: deux transitions sont considérées comme égales si leurs états de début et de fin sont identiques, les libellés des deux transitions sont égaux (ou inexistants) et les deux conditions sont identiques (ou non existantes). . Avec un décorateur, je pourrais avoir deux transitions Labeled ("foo", Conditionnel ("bar", Transition ("baz", "qux"))) et conditionnel ("Bar", Labeled ("foo" ;, Transition ("baz", "qux"))) nécessitant une égalité non locale, c'est-à-dire que les décorateurs doivent collecter toutes les données et que la transition doit comparer ces données collectées sur une base:

class Transition(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
    def get_label(self):
        return None
    def has_label(self):
        return False
    def collect_decorations(self, decorations):
        return decorations
    def internal_equality(self, my_decorations, other):
        try:
            return (self.start == other.start
                    and self.end == other.end
                    and my_decorations = other.collect_decorations())
    def __eq__(self, other):
        return self.internal_equality(self.collect_decorations({}), other)

class Labeled(object):
    def __init__(self, label, base):
        self.base = base
        self.label = label
    def has_label(self):
        return True
    def get_label(self):
        return self.label
    def collect_decorations(self, decorations):
        assert 'label' not in decorations
        decorations['label'] = self.label
        return self.base.collect_decorations(decorations)
    def __getattr__(self, attribute):
        return self.base.__getattr(attribute)

Est-ce une approche propre? Est-ce que je manque quelque chose?

Je suis surtout confus, car je peux résoudre ce problème - avec des noms de classe plus longs - en utilisant un héritage multiple coopératif:

class Transition(object):
    def __init__(self, **kwargs):
        # init is pythons MI-madness ;-)
        super(Transition, self).__init__(**kwargs)
        self.start = kwargs['start']
        self.end = kwargs['end']
    def get_label(self):
        return None
    def get_condition(self):
        return None
    def __eq__(self, other):
        try:
            return self.start == other.start and self.end == other.end
        except AttributeError:
            return False

class LabeledTransition(Transition):
    def __init__(self, **kwargs):
        super(LabeledTransition).__init__(**kwargs)
        self.label = kwargs['label']
    def get_label(self):
        return self.label
    def __eq__(self):
        super_result = super(LabeledTransition, self).__eq__(other)
        try:
            return super_result and self.label == other.label
        except AttributeError:
            return False

class ConditionalTransition(Transition):
    def __init__(self, **kwargs):
        super(ConditionalTransition, self).__init__(**kwargs)
        self.condition = kwargs['condition']

    def get_condition(self):
        return self.condition

    def __eq__(self, other):
        super_result = super(ConditionalTransition, self).__eq__(other)
        try:
            return super_result and self.condition = other.condition
        except AttributeError:
            return False

# ConditionalTransition about the same, with get_condition
class LabeledConditionalTransition(LabeledTransition, ConditionalTransition):
    pass

La classe LabledConditionalTransition se comporte exactement comme prévu - et le fait qu’il n’y ait pas de code est attrayant et je ne pense pas que MI déroute à cette taille.

Bien sûr, la troisième option consisterait simplement à tout regrouper dans une seule classe de transition avec un tas de has_label / has_transition.

Alors ... je suis confus. Est-ce que je manque quelque chose? Quelle implémentation est la meilleure? Comment gérez-vous des cas similaires, c’est-à-dire des objets qui ressemblent à un décorateur pourrait les gérer, mais ensuite, une telle méthode non locale se présente?

EDIT : Ajout de la classe ConditionalTransition. Fondamentalement, cela se comporte un peu comme le décorateur, moins l'ordre créé par l'ordre de création des décorateurs, la transition vérifie le début et la fin, la classe LabeledTransition vérifie si l'étiquette est correcte et ConditionalTransition vérifie si la condition est correcte.

Était-ce utile?

La solution

Je pense qu'il est clair que personne ne comprend vraiment votre question. Je suggérerais de le mettre en contexte et de le raccourcir. À titre d’exemple, voici une implémentation possible du modèle d’état en python. Veuillez l’étudier pour en avoir une idée.

class State(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

class Automaton(object):
    def __init__(self, instance, start):
        self._state = start
        self.transitions = instance.transitions()

    def get_state(self):
        return self._state

    def set_state(self, target):
        transition = self.transitions.get((self.state, target))
        if transition:
            action, condition = transition
            if condition:
                if condition():
                    if action:
                        action()
                    self._state = target
            else:
                self._state = target
        else:
            self._state = target

    state = property(get_state, set_state)

class Door(object):
    open = State('open')
    closed = State('closed')

    def __init__(self, blocked=False):
        self.blocked = blocked

    def close(self):
        print 'closing door'

    def do_open(self):
        print 'opening door'

    def not_blocked(self):
        return not self.blocked

    def transitions(self):
        return {
            (self.open, self.closed):(self.close, self.not_blocked),
            (self.closed, self.open):(self.do_open, self.not_blocked),
        }

if __name__ == '__main__':
    door = Door()
    automaton = Automaton(door, door.open)

    print 'door is', automaton.state
    automaton.state = door.closed
    print 'door is', automaton.state
    automaton.state = door.open
    print 'door is', automaton.state
    door.blocked = True
    automaton.state = door.closed
    print 'door is', automaton.state

la sortie de ce programme serait:

door is open
closing door
door is closed
opening door
door is open
door is open

Autres conseils

À partir du code qui a été posté, la seule différence entre Transition et Labeled Transition est le retour de get_lable () et de has_label (). Dans ce cas, vous pouvez compresser ces deux classes en une seule classe qui définit l'attribut label sur None et

.
return self.label is not None

dans la fonction has_label ().

Pouvez-vous publier le code de la classe ConditionalTransition ? Je pense que cela rendrait les choses plus claires.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top