Pregunta

Saludos, actualmente estoy refactorizando uno de mis programas y encontré un problema interesante.

Tengo transiciones en un autómata. Las transiciones siempre tienen un estado inicial y un estado final. Algunas transiciones tienen una etiqueta, que codifica una determinada acción que se debe realizar al atravesar. Ninguna etiqueta significa ninguna acción. Algunas transiciones tienen una condición, que debe cumplirse para atravesar esta condición, si no hay una condición, la transición es básicamente una transición épsilon en una NFA y se recorrerá sin consumir un símbolo de entrada.

Necesito las siguientes operaciones:

  • compruebe si la transición tiene una etiqueta
  • obtener esta etiqueta
  • agregar una etiqueta a una transición
  • compruebe si la transición tiene una condición
  • obtener esta condición
  • comprobar la igualdad

A juzgar por los primeros cinco puntos, esto suena como un decorador claro, con una transición de base y dos decoradores: Etiquetado y Condición. Sin embargo, este enfoque tiene un problema: dos transiciones se consideran iguales si su estado inicial y el estado final son iguales, las etiquetas en ambas transiciones son iguales (o no existen) y ambas condiciones son las mismas (o no existen) . Con un decorador, podría tener dos transiciones etiquetadas ("foo", Condicional ("barra", Transición ("baz", "qux")) y Condicional ("barra", etiquetada ("foo" ;, Transición (" baz " ;, " qux "))) que necesita una igualdad no local, es decir, los decoradores necesitarían recopilar todos los datos y la Transición debe comparar estos datos recopilados en una base determinada:

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)

¿Es este un enfoque limpio? ¿Me estoy perdiendo algo?

Estoy más confundido, porque puedo resolver esto - con nombres de clase más largos - usando herencia múltiple cooperativa:

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 clase LabledConditionalTransition se comporta exactamente como se esperaba, y no tener código es atractivo y no creo que MI sea confuso en este tamaño.

Por supuesto, la tercera opción sería juntar todo en una sola clase de transición con un montón de has_label / has_transition.

Entonces ... estoy confundido. ¿Me estoy perdiendo de algo? ¿Qué implementación se ve mejor? ¿Cómo maneja los casos similares, es decir, los objetos que parecen un Decorador podrían manejarlos, pero luego, aparece un método no local?

EDIT : Se agregó la clase ConditionalTransition. Básicamente, este tipo de comportamiento se comporta como el decorador, menos el orden creado por el orden de creación de los decoradores, las verificaciones de transición para el inicio y el final son correctas, la clase de Transición Etiquetada verifica que la etiqueta sea correcta y la Transición Condicional verifica que la condición sea correcta.

¿Fue útil?

Solución

Creo que está claro que nadie entiende realmente tu pregunta. Sugeriría ponerlo en contexto y hacerlo más corto. A modo de ejemplo, aquí hay una posible implementación del patrón de estado en python, estudie para obtener una idea.

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 salida de este programa sería:

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

Otros consejos

A partir del código publicado, la única diferencia entre Transición y Transición etiquetada es la devolución de get_lable () y has_label (). En cuyo caso puede comprimir estos dos una sola clase que establece un atributo de etiqueta en Ninguno y

return self.label is not None

en la función has_label ().

¿Puede publicar el código para la clase ConditionalTransition ? Creo que esto lo haría más claro.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top