LO SCAFFALE DEGLI HOBBY

Appunti On Line

AVVERTENZE da leggere attentamente


INFORMATICA

Flowchart compatti


I flowchart (diagrammi di flusso) permettono di rappresentare dei procedimenti sotto forma di disegno grafico. Un uso tipico è la rappresentazione di algoritmi informatici come il seguente.

Sono visibili: Questo flowchart può rappresentare ad esempio il flusso di esecuzione del seguente codice in linguaggio Python:
if a > b:
    istr1
    if b > c:
        istr3
    elif z == f:
        istr4
    else:
        istr5
else:
    istr2
    if z!= f:
        istr6
Con un flowchart si può rappresentare un procedimento di qualsiasi complessità, eventualmente si possono disegnare flowchart “gerarchici”, o separati in diversi pezzi.

La simbologia del disegno precedente secondo me è ottima per rappresentare procedure (o macro procedure) di complessità limitata, ma diventa difficile da seguire (e ancor peggio disegnare o modificare) se le condizioni da verificare diventano molte decine.

In tal caso la quantità di rombi, frecce, connettori può crescere fino a diventare un “gomitolo” ingestibile, in cui si perde più tempo a seguire le frecce che la logica generale.

Tra l’altro se le condizioni sono composte da molte sotto condizioni (legate in AND/OR tramite operatori logici) lo spazio per scriverle all’interno dei rombi è davvero scomodo, e tocca disegnare rombi assurdamente larghi (come di fatto vedo in molte pubblicazioni).



Compattiamo...

Ci sono molti tipi di rappresentazioni grafiche e diagrammi. Mi sono imbattuto per caso in questa idea (grazie ad un utente del forum Arduino). Non so se ha un nome o se venga utilizzata in pratica, però mi sembra davvero ottima per eliminare i “difetti” appena elencati.

Ecco l’ equivalente compatto del flowchart precedente:

I rami else senza istruzioni non serve rappresentarli (salvo forse casi realmente ambigui), e resta sottointesa la loro chiusura in corrispondenza della chiusura della struttura condizionale (freccetta in basso). Ad esempio questi due casi sono ben diversi:

A sinistra ci sono due if annidati: la condizione b>c viene valutata solo se la prima condizione a>b risulta vera, il ramo else della prima condizione si chiude con la freccetta in basso.

A destra ci sono due if indipendenti: la condizione b>c viene valutata sempre, il ramo else della prima condizione si chiude con la freccetta sotto istr1.


Questi sono i codici Python e C corrispondenti ai due casi:
if a > b:
    istr1
    if b > c:
        istr2

if a > b:
    istr1

if b > c:
    istr2
if (a > b)
{
    istr1;
    if (b > c)
    {
        istr2;
    }
}
if (a > b)
{
    istr1;
}
if (b > c)
{
    istr2;
}



Cicli

Anche i cicli si possono rappresentare in modo compatto. Il while esegue continuamente le istruzioni sotto di lui finché la condizione risulta vera (pertanto non serve rappresentare una freccia che torna indietro).

Il do/while richiede invece un percorso che torna indietro per indicare l’inizio del blocco di istruzioni, ma si usa più raramente, tanto che in Python non è neppure presente.

La condizione falsa (oppure un break) porta all’uscita e chiusura della struttura.




Ottimizzazione

“Accidentalmente” la lettura a colpo d’occhio del flusso, sempre dall’alto in basso per le operazioni, e da sinistra a destra per le condizioni, mi sembra permetta di scorgere meglio possibili ottimizzazioni ed arrivare al “codice minimo” dei selettori.

Ad esempio questo è un algoritmo che mi è realmente servito per ricevere semplici messaggi dalla porta seriale delimitati da un ‘#’ iniziale e un ‘*’ finale:
Inizialmente era costruito con diversi if annidati scritti seguendo una certa logica di pensiero. Rappresentato visivamente con un flowchart compatto si vedeva chiaramente che si poteva accorpare tutto in un unico selettore riducendo i livelli di annidamento delle strutture.

Codice in linguaggio C++:
boolean ricevuto()
{
    static uint8_t stato = 0;
    boolean rxok = false;
    while(Serial.available()  &&  !rxok)
    {
        char chr = Serial.read();
        if(stato == 0  &&  chr == '#')
        {
            buf_i = 0;
            stato = 1;
        }
        else if(stato == 1  &&  chr == '*')
        {
            stato = 0;
            if(buf_i > 0)
                rxok = true;
        }
        else if(stato == 1  &&  buf_i == MAXBUF)
            stato = 0;
            
        else if(stato == 1)
            buf[buf_i++] = chr;
    }
    return rxok;
}
Di seguito il flowchart compatto corrispondente. In questo specifico caso non serve neppure rappresentare le frecce di chiusura della struttura selettore, perché non vi è alcun dubbio che il percorso riporta in tutti i casi alla valutazione della condizione while.


Per fare questo disegno ci vuole meno tempo rispetto a disegnare un flowchart “tradizionale”. Seguire il flusso delle operazioni è elementare, perché basta scendere dall’alto in basso “entrando” nella colonna corrispondente alla prima condizione vera. Allo stesso tempo si vede distintamente tutto ciò che viene controllato (condizionato) da un selettore o da un ciclo.

Se vogliamo trovare un piccolo difetto, quando ci sono selettori con molte condizioni si occupa un notevole spazio orizzontale.



All'indice principale | Al vecchio sito