Pagine

03 luglio 2011

Una macchina a stati finiti

Buon pomeriggio gioventù,
Ieri sera mi sono imbattuto nell'avventurosa creazione di un automa a stati finiti usando Python. La cosa mi ha messo un po'in difficoltà, soprattutto perché viene difficile non pensare all'automa come ad un circuito ma come un software. In fondo con poche porte logiche e qualche flip flop si fanno anche automi complessi.
In ogni caso, parlandone col mio buon amico e consulente informatico Ennio ne sono venuto a capo :-)
Facciamo un esempio:
Supponiamo di avere un automa fatto da 3 stati:
IDLE, WORK, COMMUNICATION
gli input che uniscono i tre stati sono:
- lavora
- dimmi
- fatto


Fig.1 - Diagramma dell'automa dell'esempio

Partiamo quindi col definire la struttura base di uno stato:

class State:
    def lavora(self):
        return None
    def dimmi(self):
        return None
    def fatto(self):
        return None

Questa classe apparentemente inutile è l'equivalente di una classe astratta di Java.
Le classi che rappresentano gli stati non faranno altro che ereditare i metodi di questa classe in modo da implementare solo quelli che sono realmente necessari.
In pratica ciascuno stato implementerà solo i metodi relativi alle sue transizioni.

class IDLE(State):
    
    def lavora(self):
        return "WORK"
    
class WORK(State):
        
    def dimmi(self):
        return "COMMUNICATE"
    
class COMMUNICATE(State):
        
    def fatto(self):
        return "IDLE"

E fino a qui direi che è tutto chiaro. In pratica i metodi associati ad una transizione restituiscono il nome dello stato successivo, altrimenti restituiscono None.
Implementiamo infine la classe che fa girare l'automa vero e proprio:

class Machine:
    current_state = IDLE()

    def esegui(self,messaggio):
        try:
            s = getattr(self.current_state,messaggio)()
            if s<>None and s == "WORK":
                self.current_state=WORK()
            if s<>None and s == "COMMUNICATE":
                self.current_state=COMMUNICATE()
            if s<>None and s == "IDLE":
                self.current_state=IDLE()
            print(s)
        except:
            print(":-P")

    def current(self):
        if isinstance(self.current_state,IDLE):
            return "IDLE"
        if isinstance(self.current_state,WORK):
            return "WORK"
        if isinstance(self.current_state,COMMUNICATE):
            return "COMMUNICATE"


In pratica come funziona?
Nel momento in cui da istanzio la classe Machine creo l'automa:
m = Machine()
Io stato corrente è IDLE e facendo da riga di comando:
m.current()
A video ci viene stampato "IDLE".
e se voglio cambiare stato?
m.esegui('lavora')
la macchina cambia stato e 
m.current() 
restituirà ora "WORK".
Ma come funziona esegui?
Sfrutta l'introspezione, una caratteristica molto potente di Python.
getattr cerca nell'oggetto referenziato da self.current_state un attributo che si chiama come la stringa passata e se è un metodo lo esegue.
Se il metodo non esiste si genera un'eccezione e viene stampata una linguaccia.
Ora... se il metodo viene eseguito si ha una transizione e l'automa cambia stato,
altrimenti ci prendiamo la nostra linguaccia :-P
Carino vero?
Saluti a tutti!


Nessun commento:

Posta un commento