Domanda

Saluti, attualmente sto eseguendo il refactoring di uno dei miei programmi e ho riscontrato un problema interessante.

Ho Transizioni in automi. Le transizioni hanno sempre uno stato iniziale e uno finale. Alcune Transizioni hanno un'etichetta, che codifica per una determinata Azione che deve essere eseguita su attraversamento. Nessuna etichetta significa nessuna azione. Alcune transizioni hanno una condizione, che deve essere soddisfatta per attraversare questa condizione, se non c'è alcuna condizione, la transizione è fondamentalmente una transizione epsilon in un NFA e sarà attraversata senza consumare un simbolo di input.

Ho bisogno delle seguenti operazioni:

  • controlla se la transizione ha un'etichetta
  • ottieni questa etichetta
  • aggiungi un'etichetta a una transizione
  • controlla se la transizione ha una condizione
  • ottieni questa condizione
  • verifica l'uguaglianza

A giudicare dai primi cinque punti, questo suona come un chiaro decoratore, con una transizione di base e due decoratori: Etichetta e Condizione. Tuttavia, questo approccio ha un problema: due transizioni sono considerate uguali se il loro stato iniziale e finale sono uguali, le etichette in entrambe le transizioni sono uguali (o inesistenti) ed entrambe le condizioni sono uguali (o non esistenti) . Con un decoratore, potrei avere due transizioni con etichetta (" foo " ;, condizionale (" bar " ;, transizione (" baz " ;, " qux "))) e condizionale (" bar " ;, con etichetta (" foo " ;, Transizione ("quotaz", "quot" qux "))) che necessitano di un'uguaglianza non locale, ovvero i decoratori dovrebbero raccogliere tutti i dati e la transizione deve confrontare questi dati raccolti su un set-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)

È un approccio pulito? Mi sto perdendo qualcosa?

Sono per lo più confuso, perché posso risolverlo - con nomi di classe più lunghi - usando l'ereditarietà multipla 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 classe LabledConditionalTransition si comporta esattamente come previsto - e non avere codice all'interno è allettante e non credo che MI sia confuso a queste dimensioni.

Naturalmente, la terza opzione sarebbe quella di trasformare tutto in una singola classe di transizione con un gruppo di in has_label / has_transition.

Quindi ... sono confuso. Mi sto perdendo qualcosa? Quale implementazione sembra migliore? Come gestite casi simili, ovvero oggetti che sembrano Decoratori potrebbero gestirli, ma poi viene fuori un metodo non locale?

Modifica : Aggiunta la classe ConditionalTransition. Fondamentalmente, questo tipo di comportamento si comporta come il decoratore, meno l'ordine creato dall'ordine di creazione dei decoratori, i controlli di transizione per l'inizio e la fine sono corretti, la classe LabeledTransition verifica che l'etichetta sia corretta e ConditionalTransition controlla che la condizione sia corretta.

È stato utile?

Soluzione

Penso sia chiaro che nessuno capisce davvero la tua domanda. Suggerirei di inserirlo nel contesto e di renderlo più breve. Ad esempio, ecco una possibile implementazione del modello di stato in Python, per favore studiatelo per avere un'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

l'output di questo programma sarebbe:

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

Altri suggerimenti

Dal codice che è stato pubblicato, l'unica differenza tra Transizione e Transizione con etichetta è il ritorno di get_lable () e has_label (). Nel qual caso puoi comprimere questi due una singola classe che imposta un attributo label su None e

return self.label is not None

nella funzione has_label ().

Puoi pubblicare il codice per la classe ConditionalTransition ? Penso che ciò renderebbe più chiaro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top