Lo pseudocodice per la scrittura di algoritmi: un metodo efficace anche nella didattica per le disabilità visive

Tempo di lettura: 10 minuti

Oggi vi parlo di algoritmi e pseudocodice, in un ottica un po’ diversa dal solito. Ho conosciuto questi termini durante gli studi tecnici come perito informatico, capendone le potenzialità come utente ipovedente. Ne riconosco l’efficacia ancora oggi da non vedente e rilancio dichiarando che per una persona con disabilità visiva, la tecnica della scrittura di pseudocodice è assolutamente efficace, rappresentando comunque una metodologia usata spesso anche da sviluppatori normodotati. Quindi non è un metodo esclusivo, ma una tecnica nata per tutti che nello specifico ci è molto utile.

Una definizione di algoritmo

Un algoritmo è una serie di istruzioni ordinate e ben definite che descrivono come risolvere un determinato problema o compiere una determinata operazione. In altre parole, un algoritmo è un metodo sistematico per risolvere un problema, composto da una sequenza di passi logici che, se eseguiti correttamente, risolveranno il problema o compiranno l’operazione richiesta.
Un algoritmo è considerato efficace se risolve il problema in modo efficiente, ovvero utilizzando il minor numero di risorse possibile, come tempo di calcolo, memoria o spazio di archiviazione. Gli algoritmi vengono spesso analizzati in termini di complessità computazionale, che descrive quanto sia efficiente un algoritmo in base alla dimensione del problema da risolvere.
Nel campo informatico e in particolare nel mondo della programmazione, la definizione di algoritmo ci è utile per la risoluzione di varie tipologie di problemi. Infatti, i passi necessari alla risoluzione logica di un dato problema, possono essere convertiti in una sequenza di istruzioni elementari, ordinate, non ambigue e finite.
La capacità di definire tali passi e la conoscenza approfondita degli stessi è chiamato “pensiero computazionale”.
In una fase successiva, tali istruzioni, saranno convertite utilizzando la sintassi del linguaggio di programmazione scelto in fase di progettazione.
Gli algoritmi possono essere utilizzati in molti campi, dalla matematica alla scienza dei dati, dall’informatica alla fisica, e così via.

Un esempio preso dalla vita reale

Potrei proporvi un esempio che appartiene strettamente al mondo della matematica, ma preferisco parlarvi di qualcosa di manuale che fa parte, quasi certamente, della nostra quotidianità. Infatti, ogni giorno, quando compiamo azioni o attività, inconsciamente, utilizziamo degli algoritmi per portarle a termine, tramite una metodologia a noi nota, quella secondo noi più adatta per raggiungere il risultato finale.
Avete mai pensato ai passi necessari e ordinati per preparare una tazza di caffè con una moka?
Seppur con delle piccole variazioni e ottimizzazioni, potremmo ipotizzare i seguenti passi:

  1. prendo la moka;
  2. la svito ;
  3. apro il rubinetto dell’acqua;
  4. lavo la moka sotto l’acqua corrente;
  5. riempio il serbatoio dell’acqua in base a quanto voglio intenso il caffè;
  6. chiudo il rubinetto dell’acqua;
  7. inserisco il filtro che conterrà il caffè;
  8. prendo il barattolo del caffè macinato;
  9. con un cucchiaino, riempio il filtro tanto quanto basta per avere un caffè più o meno forte;
  10. ripongo il barattolo del caffè;
  11. avvito la moka;
  12. appoggio la moka chiusa sul fornello ancora spento;
  13. accendo il fuoco e regolo la fiamma;
  14. attendo che il caffè sia pronto;
  15. spengo il fuoco;
  16. prendo una tazza;
  17. prendo la moka;
  18. verso il caffè nella tazza;
  19. ripongo la moka.

Se ci siamo chiesti il motivo per cui sappiamo preparare un caffè, una risposta possibile è che l’esperienza nel prepararlo, magari ogni mattina per colazione, ci ha fatto migliorare nei passi necessari, moderando la quantità di caffè, lo prepariamo per soddisfare meglio i nostri gusti, mettendo più o meno acqua, ne gestiamo l’intensità. Naturalmente, il nostro risultato finale è il caffè stesso, del quale sappiamo indicare un indice di gradimento ed in base ad esso, la prossima volta, cercheremo di migliorarlo.
Ci capiterà spesso di avere definito una soluzione che crediamo ottimale, per poi accorgerci che ciò che avevamo dedotto era assolutamente migliorabile. Il bello è che potremo migliorare il nostro algoritmo, rendendolo più funzionale, questa prende il nome di fase di ottimizzazione. Anche l’algoritmo appena scritto può essere migliorato e generalizzato il più possibile. Infatti, ove possibile e utile, dovremo sempre cercare di essere generici nel definire i nostri algoritmi, in modo da poterli riutilizzare in altri ambiti. Se ad esempio scriviamo un algoritmo per preparare il caffè con la moka, vogliamo che funzioni sempre, anche in diversi ambiti. Ad esempio, se siamo a casa, in campeggio o nella casa in montagna e vogliamo preparare il caffè, non scriveremo un nuovo algoritmo, ma riutilizzeremo sempre quello proposto. Se in fase di scrittura della pseudocodifica inserissimo delle istruzioni eseguibili solo in un certo ambito o ambiente, non saremo in grado di riutilizzarlo. Il concetto di riuso del codice è centrale nella programmazione.

Lo pseudocodice

Lo pseudocodice (o pseudocodifica) è una modalità testuale per la rappresentazione di un algoritmo. Tale linguaggio intermedio, non rispetta degli standard definiti da enti preposti e rappresenta il passo precedente alla scrittura vera e propria delle istruzioni in un linguaggio macchina. Anche lo pseudocodice, come i linguaggi veri e propri, deve rispettare una sintassi e delle regole che proveremo a definire.
Quando si raggiunge una certa esperienza nel campo della programmazione, lo pseudocodice torna utile per prendere appunti sulla logica che dovrà avere una certa porzione di codice, è un modo di scrivere universale, chiaro e utile anche ad altri sviluppatori con cui si condivide il lavoro.

Un primo semplice esempio

Senza darvi altre indicazioni teoriche, proviamo a scrivere l’algoritmo che determina se un numero è pari o dispari, tramite il seguente pseudocodice:

  1. begin
  2. print “Inserisci un numero naturale n”
  3. input n dall’utente
  4. if n%2 == 0 then (se il resto della divisione per 2 è 0 allora):
    1. print “Il numero è pari”
  5. else
    1. print “Il numero è dispari”
  6. end if
  7. end

Prestate attenzione al comando “input n”, infatti esso introduce un concetto fondamentale, quello di variabile. L’elemento n è un contenitore di un certo dato, tale contenitore rimane immutato fino a che non diamo un comando che ne alteri il contenuto. L’abbiamo chiamato n, ma lo potevamo anche chiamare diversamente, ad esempio numero o x, siamo noi a decidere questi nomi.
Come potete vedere, questo linguaggio è più rigoroso rispetto a quello utilizzato per l’algoritmo per la preparazione del caffè. Vengono di certo rispettate delle regole e viene utilizzata una specifica sintassi.
Questa metodologia testuale,è assolutamente accessibile ed utilizzabile da non vedenti e ipovedenti. Si integra in modo ottimale con il lettore di schermo,perchè la sintassi la si può scrivere in un qualsiasi editor di testo (come blocco note o notepad++)e non presenta alcun riferimento grafico.
L’unico passaggio necessario richiesto, è quello di pensare a step, quando si affronta la risoluzione di un problema. Molti di noi già lo fa, senza rendersene conto.
Posso suggerirvi di iniziare ad aprire il vostro editor di testo preferito e partire a elencare i passi necessari alla risoluzione del dato problema. Scriveteli in un linguaggio non formale, ma descrittivo.
Successivamente, accertatevi che gli step siano ordinati in senso logico e non preoccupatevi di eventuali step ripetuti, infatti vedremo come “riciclare” degli step già definiti, in modo da normalizzare il flusso del nostro algoritmo.
Se sono invece presenti passaggi che contengono più operazioni, bisogna cercare di separarli, creando così più step, in modo che sia più semplice convertirli nelle fasi successive.
Fatto ciò, si può passare alla fase di conversione nel linguaggio di pseudocodifica e solo successivamente convertiremo il tutto nel linguaggio scelto.

Alcuni concetti base per comprendere meglio: variabile, costrutto, condizione e operatori logici

In programmazione, una variabile è un contenitore dove viene memorizzato un valore o un insieme di valori. Le variabili vengono utilizzate per memorizzare temporaneamente informazioni all’interno del programma, in modo da poterle richiamare e manipolare in modo efficiente. Per capire meglio, puoi immaginare una scatola dove puoi mettere oggetti diversi. In una variabile possiamo inserire un valore numerico, una parola, un elenco di numeri o di parole o qualsiasi altro tipo di informazione.
Per esempio, supponiamo che ti chieda di scrivere un programma che calcola l’area di un cerchio. La formula per il calcolo dell’area richiede di utilizzare il valore del raggio del cerchio, inoltre ci serve il valore di pigreco. Quindi, definiresti una variabile chiamata “raggio” e gli attribuiresti il valore numerico del raggio. Poi potremmo definire una variabile pigreco che vale sempre 3.14. Successivamente, utilizzando le due variabili nel tuo calcolo, sarebbe facile trovare l’area del cerchio.
In sostanza, una variabile è un modo per salvare una informazione all’interno di un programma in modo da poterla richiamare e manipolare in futuro.
Cambiando argomento, il costrutto è un blocco di codice che serve per eseguire una specifica operazione o una serie di azioni. Si possono immaginare i costrutti come “mattoni” di base che vengono utilizzati per creare programmi complessi. Ad esempio, un costrutto comune è il ciclo, che permette di eseguire ripetutamente una serie di istruzioni senza doverle ripetere manualmente. Dovete immaginare il costrutto come un recinto dentro il quale ci sono delle istruzioni o dentro il quale ci potrebbe essere anche un altro costrutto. Il recinto definisce quali sono le istruzioni del costrutto, quindi quali istruzioni vengono eseguite se ci sono le condizioni per entrare in quel costrutto.
Le condizioni sono il fondamento per controllare il flusso del programma e determinare l’esecuzione di specifiche azioni. Esse ci permettono di valutare se una specifica relazione tra le variabili è vera o falsa, aprendo la porta a una vasta gamma di possibilità.
Ecco alcuni esempi di condizioni:

  • verificare se un numero è pari o dispari;
  • controllare se due variabili contengono lo stesso valore;
  • stabilire se un’età è maggiore o minore di un certo limite.

Per costruire le condizioni, utilizziamo gli operatori di confronto. Questi operatori ci permettono di compilare una relazione tra due valori, restituendo un risultato di vero o falso. I principali operatori di confronto sono:

  • maggiore (>): Valuta se il primo valore è maggiore del secondo;
  • minore (<): Valuta se il primo valore è minore del secondo;
  • uguale a (==): Valuta se i due valori sono uguali;
  • diverso da (!=): Valuta se i due valori sono diversi;
  • maggiore o uguale a (>=): Valuta se il primo valore è maggiore o uguale al secondo;
  • minore o uguale a (<=): Valuta se il primo valore è minore o uguale al secondo.

Mentre invece gli operatori logici ci permettono di combinare due o più condizioni in un’unica espressione più complessa. I principali operatori logici sono:

  • not (non): inverte il valore di verità di una condizione. Se la condizione è vera, la trasforma in falsa, e viceversa;
  • and (e): restituisce vero solo se entrambe le condizioni sono vere;
  • or (oppure): restituisce vero se almeno una delle due condizioni è vera.

In un istruzione condizionale “if” (se) possiamo combinare più condizioni utilizzando gli operatori logici. Ad esempio, immaginiamo di voler verificare se un numero è maggiore o uguale a 10 e se è anche pari. In pseudocodice, potremmo scrivere:
if (numero >= 10 and numero % 2 == 0) then.
In questo esempio, l’operatore “and” combina le due condizioni: “numero >= 10” e “numero % 2 == 0”. Solo se entrambe le condizioni sono vere, le istruzioni all’interno del blocco “then”verranno eseguite.

Definiamo un nostro standard

Nel definire un nostro standard, dobbiamo sempre tenere conto delle istruzioni che un linguaggio di programmazione ci mette a disposizione. Non dobbiamo inoltre dimenticare che un algoritmo, rappresentato tramite lo pseudocodice, dovrebbe poter essere tradotto in qualsiasi linguaggio di programmazione e non solo in quello che vogliamo utilizzare
Definiamo il seguente standard e utilizziamolo per i nostri algoritmi:

  • begin : punto di inizio del nostro algoritmo;
  • end : termine del nostro algoritmo;
  • # : simbolo con il quale possiamo scrivere dei commenti nella nostra pseudocodifica. Tutto ciò che scriveremo dopo il simbolo cancelletto non farà parte del flusso del programma;
  • print “stringa” o nome_variabile : output (stampa su schermo), leggibile dall’utente;
  • input nome_variabile : input inserito dall’utente;
  • nome_variabile = valore, espressione o procedura : assegna un valore ad una data variabile. Tale valore può essere fisso, come un numero o una stringa, oppure può derivare da un’espressione o dall’esecuzione di una procedura. Ciò che definiamo a destra dell’uguale, verrà assegnato al contenitore a sinistra dell’uguale. Ad esempio se scriviamo cognome = “Albano”, stiamo assegnando alla variabile cognome il valore stringa Albano;
  • + : operatore aritmetico di somma;
  • – : operatore aritmetico di sottrazione;
  • * : operatore aritmetico di prodotto;
  • / : operatore aritmetico di divisione;
  • // : operatore aritmetico di divisione per ottenere solo la parte intera;
  • % : operatore per avere il modulo tra due numeri, cioè il resto della divisione tra i due numeri;
  • = : operatore aritmetico usato per assegnare un risultato o un valore;
  • not : parola chiave usata per negare un valore o condizione. Da scrivere prima del valore, condizione o espressione da negare;
  • != : operatore utilizzato per indicare che un valore o condizione è diversa da qualcos’altro;
  • == : operatore usato nelle condizioni per indicare una uguaglianza in un confronto;
  • < o > : simbolo di minore e maggiore, usati per confrontare i valori nelle condizioni dei vari costrutti condizionali o cicli condizionati;
  • and : usata come una congiunzione, si utilizza per unire più condizioni, indicando così che devono essere entrambe soddisfatte per validare la condizione;
  • or : utilizzata come oppure, indica che tra due condizioni, basta che ne sia valida una per validare il confronto;
  • if (condizione) then {istruzione1} else {istruzione2} end if: questa sintassi rappresenta un controllo condizionale, di certo uno dei più utilizzati per definire la logica di un algoritmo. Questo tipo di istruzioni possono essere annidate per aumentare la complessità dei controlli sulle condizioni. Ad esempio, si può inserire un ulteriore if all’interno del ramo else, creando così una serie annidata di controlli condizionali;
  • while (condizione) {istruzioni} end while : fino a che una data condizione è verificata, ripeti le istruzioni. Questa sintassi definisce ciò che è chiamato ciclo condizionato;
  • do {istruzioni} while (condizione) end do while : ripeti le istruzioni fino a che una data condizione è verificata. Anche questa sintassi esprime il concetto di ciclo condizionato, con la differenza che le istruzioni vengono eseguite almeno una volta per poi ripetersi se la condizione è ancora soddisfatta;
  • breack : termina il ciclo improvvisamente sulla base di una data condizione. Da usare con cautela, dato che è sempre meglio progettare un ciclo in modo che termini a fine iterazioni e non bruscamente. Nonostante ciò, ci sono alcuni casi, in cui può essere utile e funzionale interromperlo prima;
  • function nome_funzione(possibili_parametri) {istruzioni}return (eventuale_valore_di_ritorno) end function: una procedura (sinonimo di metodo o funzione)contiene una porzione di codice, solitamente riutilizzabile ed invocabile nel flusso dell’algoritmo. Una procedura può ricevere dei parametri quando invocata e può ritornare al chiamante un valore.
  • function_name(possibili_parametri) : invoca una data procedura, richiamandola per nome, utile anche per il riuso di porzioni di codice ripetute. A livello teorico si rimanda al concetto di ricorsione;

Come avrete notato, alcune istruzioni avanzate utilizzano le parentesi graffe per racchiudere le istruzioni che appartengono a quel costrutto. Queste parentesi servono per delimitare chiaramente il blocco di codice e renderlo più leggibile.
Con la pratica, è possibile utilizzare l’indentazione per omettere le parentesi graffe, in questo modo la nostra pseudocodifica sarà ancora più generale. L’indentazione consiste nell’allineare le istruzioni sotto il costrutto di appartenenza, spostando il testo verso destra di un certo numero di caratteri (solitamente 4 spazi o una tabulazione). Questo metodo rende il codice più organizzato e visivamente più chiaro, facilitando la comprensione della struttura del programma.
Oltre a quanto già detto, è utile ricordare che nella pseudocodifica è possibile numerare le istruzioni per indicarne l’ordine di esecuzione. Come mostrato nell’esempio per l’identificazione dei numeri pari e dispari, questa pratica è particolarmente vantaggiosa all’inizio, quando si inizia a prendere confidenza con la sintassi dei costrutti condizionali e dei cicli.
Con la pratica, la numerazione manuale delle righe nella pseudocodifica può essere abbandonata. Gli editor di testo, infatti, offrono una funzione integrata che permette di visualizzare il numero di riga in cui si sta lavorando.
Il traguardo finale è la stesura di una pseudocodifica che sia concisa, chiara e leggibile, utilizzando l’indentazione al posto delle parentesi graffe e omettendo del tutto i numeri di riga.

Cenni sulle procedure

Una procedura, un metodo o una funzione rappresentano una porzione di codice che esegue un’operazione specifica racchiusa in un blocco di istruzioni.
Le procedure sono molto utili nella programmazione, poiché permettono di eseguire un blocco di codice più volte, senza doverlo usare ripetutamente ogni volta che lo si deve eseguire. Inoltre, una procedura può accedere a variabili e dati che sono stati definiti fuori di essa, spesso passati come parametri all’inizio dell’esecuzione.
In generale, l’uso delle procedure, dei metodi o delle funzioni è importante per organizzare il codice in un modo sorvegliato e mantenibile, specialmente per programmi di grandi dimensioni che contengono molte funzioni e dati.
Un esempio pratico di funzione potrebbe essere una semplice funzione che, passati due numeri, restituisce il loro prodotto.
In pseudocodice, la funzione potrebbe essere descritta in questo modo:

  1. calcola_prodotto(num1, num2)
    1. prodotto = num1 * num2
    2. return prodotto
  2. end function

Questa funzione può essere chiamata in qualsiasi momento e le sue istruzioni saranno eseguite.

In conclusione

Potremmo definire una pseudocodifica più dettagliata e rigorosa, ma secondo me, con questi costrutti di base, possiamo scrivere con chiarezza tutti gli algoritmi di cui avremo bisogno.
Se capiamo questo approccio, avremo ben chiara la logica dell’algoritmo e tradurre il tutto in un linguaggio macchina sarà semplice.
Questa metodologia è anche molto comoda per testare in modo rapido la logica del nostro flusso di istruzioni. Infatti possiamo prevedere diversi casi in cui cambiando i valori dati in ingresso avremo diversi risultati finali che dovranno rispettare le condizioni del nostro algoritmo. Se questi test dovessero fallire, sarebbe inutile concentrarsi nello scrivere il codice macchina, varrebbe la pena prima dedicarsi alla stesura di uno pseudocodice funzionante.

Abbiamo visto cosa si intende per algoritmo e come lo si converte in uno pseudocodice ben definito. Il passo successivo che rimane da compiere è la traduzione dello stesso in un linguaggio di programmazione. Non ho inventato io questi metodi, ho semplicemente preso spunto dal mio vissuto e penso che questa metodologia, applicata alla disabilità visiva, possa aiutare per ottenere ottimi risultati nella risoluzione di qualsiasi tipo di problema. Molto di ciò che le persone normodotate fanno con la vista, noi lo dobbiamo fare con la mente, dobbiamo avere dei pensieri ben ordinati, ridotti ai minimi termini, la logica del flusso di un listato di codice deve essere disegnata nella nostra mente ed io utilizzo questo metodo semplice e potente.

Materiale e esercizi proposti

  • provare a riscrivere l’algoritmo della moka con la pseudocodifica. Immaginatelo come un flusso in cui vengono date delle istruzioni all’utente tramite il comando “print” e vengono inseriti dall’utente ingredienti tramite “input”;
  • crea un programma in pseudocodice che chieda all’utente tre voti e ne calcoli la media. Utilizza le variabili “grade1”, “grade2”, “grade3” e “average” per memorizzare rispettivamente i valori immessi dall’utente e il risultato del calcolo;
  • scrivere in pseudocodifica un programma che dato in input un orario, nel formato hh:mm:ss, lo trasformi in secondi. L’utente darà in input separatamente i tre valori hh,mm e ss. Verificare che l’input dell’utente sia corretto, cioè che ore, minuti e secondi abbiano dei valori ammessi;
  • progettare una calcolatrice per effettuare le 4 operazioni fondamentali (addizione, sottrazione, moltiplicazione e divisione). È un progetto apparentemente semplice, ma presenta delle piccole insidie dovute alle proprietà fondamentali di queste potenti operazioni. Supponiamo che i numeri inseriti in input siano interi positivi >= 0 (l’insieme dei numeri naturali). Progettate voi liberamente la logica, le fasi di input e output e tutti i controlli da effettuare sui numeri inseriti e sulla fattibilità delle operazioni;
  • Scrivi un programma in pseudocodifica che generi un numero casuale compreso tra 1 e 100 e chieda all’utente di indovinare il numero. Ipotizza di creare quindi una funzione che genera un numero casuale, possiamo ipotizzare di chiamarla random().
    L’utente ha a disposizione un massimo di 10 tentativi per indovinare il numero. Il programma deve fornire un feedback all’utente indicando se il numero inserito è troppo alto o troppo basso rispetto al numero generato. Se l’utente indovina il numero, il programma deve terminare e fornire un messaggio di congratulazioni. Se l’utente esaurisce i tentativi senza indovinare il numero, il programma deve terminare e fornire il numero segreto. Potremmo anche ipotizzare di fare scegliere il livello di difficoltà del gioco, da 1 a 3, più il livello è alto e meno saranno i tentativi possibili;
  • Implementazione di algoritmi materiale online libero su Wiki Books: una volta fatta pratica nell’analisi di un problema e capiti i concetti per la stesura di uno pseudocodice, potrete procedere nell’analizzare gli algoritmi proposti, progettandoli e scrivendoli a modo vostro e confrontando le soluzioni proposte. Consiglio di iniziare con l’algoritmo per l’elevazione a potenza e con l’algoritmo di Euclide, le versioni non ricorsive. Man mano che si andrà avanti con gli studi, si riusciranno a risolvere anche quelli più complessi.

Sostieni questo blog con una donazione

Se ti piace ciò che faccio e lo trovi utile, fai una donazione con Paypal oppure usa SatisPay.

Grazie per il tuo supporto!

Pubblicato da

Alessandro Albano

Analista programmatore, formatore e tiflologo.

2 commenti su “Lo pseudocodice per la scrittura di algoritmi: un metodo efficace anche nella didattica per le disabilità visive”

I commenti sono chiusi.