Studio degli algoritmi

Requisiti

  • Pazienza
  • Conoscenze matematiche medie
  • Aver programmato
  • Tanto tempo da perdere :P

Introduzione

Il mondo della programmazione è pieno di sfide che danno vita ad algoritmi più o meno efficienti.
Ma come si fa a stabilire quando un algoritmo è ottimale? Cercherò di spiegarlo rendendo il tutorial il più comprensibile e facile possibile.

Cos’è un algoritmo?

Un algoritmo è composto da una serie di passi (righe di codice) che risolvono un qualsiasi problema computazionale.
Che problema computazionale? Ordinamento di dati, gestione degli ordini di un sito di e-commerce, conversione di dati, ecc…
Ogni volta che sbattiamo la testa contro un problema esso sarà risolvibile tramite un algoritmo (più o meno banale).

1,2,3 via…

Uno dei problemi storici riguarda come ordinare n numeri memorizzati in un array o vettore.
Quando giochiamo a carte siamo abituati ad ordinare le carte che abbiamo in mano seguendo un determinato procedimento: ogni carta nuova che prendiamo con la mano destra, la inseriamo nella posizione corretta nel ventaglio di carte (che è già ordinato) che teniamo nell’altra mano.
Se decontestualizziamo questa nostra conoscenza abbiamo appena creato l’Insertion Sort (che usa appunto un approccio incrementale).
Il codice lo scriverò in Actionscript 1 perchè risulta molto simile allo pseudo-codice con cui andrebbero scritti gli algoritmi.

InsertionSort = function (a) {
for (i=1; i=0 and keyi punta alla carta corrente (mano destra) che va messa nelle posizione giusta tra le carte già ordinate che vanno da 0 a i-1 (mano sinistra).
Le carte che sono ancora sul tavolo sono invece rappresentate dai numeri compresi tra i+1 e n.
Ogni algoritmo andrebbe studiato pensando a 3 diversi casi:

  • Caso migliore
  • Caso medio
  • Caso peggiore

Tra questi 3 casi il più importante è quello peggiore che ci darà un indicazione precisa di come l’algoritmo se la caverà nella situazione meno conveniente (potendo quindi affermare che l’algoritmo non sarà più lento di così).
Limitatamente a questo algoritmo il caso peggiore l’abbiamo quando i numeri sono ordinati in maniera decrescente perchè il ciclo while interno eseguirà il massimo numero di iterazioni possibili (mentre quelle del ciclo for sono indipendenti dall’ordine dei numeri in input).
Se ad esempio prendiamo la sequenza 5,4,3,2,1 e la ordiniamo con Insertion Sort avremo 4 iterazioni del ciclo for e 10 totali (1+2+3+4) del ciclo while, mentre con la sequenza inversa (il caso migliore) avremo rispettivamente 4 e 0 iterazioni; una bella differenza no?
Vi anticipo quindi che l’algoritmo InsertionSort nel caso peggiore ha una complessità quadratica (n^2), mentre nel caso migliore sarà lineare (n).
EH, cosa?!?

Studio di complessità

Per studiare la complessità di un algoritmo viene associato un peso a ciascuna riga che viene poi moltiplicato per il numero di volte che essa viene eseguita.
Nella figura sottostante sono indicati i costi e il numero di iterazioni di ciascuna riga (da notare come nella testa di ogni ciclo ci sia un iterazione in più dovuta al controllo della guardia):

Complessità Insertion Sort

Non preoccupatevi, dopo aver visto una cosa simile mi era venuto l’istinto suicida anche a me ;)
Il costo complessivo dell’algoritmo è la somma del prodotto tra le costanti di riga e il numero di volte che la riga interessata viene eseguita, ottenendo quindi:

Calcoli complessità

Per non appesantire il tutorial mi limito a dire che le costanti si possono tranquillamente ridurre ad una costante di massima, mentre la sommatoria, come alcuni di voi avranno notato, è la sommatoria aritmetica che trova la sua risoluzione nella formula (n^2-n)/2.
Nel caso peggiore avremo che Tj sarà proprio uguale a j (perchè ogni volta che inseriamo la carta nuova, dobbiamo traslarle tutte) portando l’algoritmo ad avere una complessità quadratica; mentre nel caso migliore il ciclo while interno non esegue alcuna iterazione rendendo la complessità lineare.
Dicendo “complessità quadratica” o “complessità lineare” indico la complessità in termini asintotici permettendo di valutare gli algoritmi indipendentemente dalla piattaforma di esecuzione (grazie alle costanti).

Morale

Lo scopo di questo tutorial non è quello di insegnare il calcolo della complessità di qualsiasi algoritmo (ci mancherebbe! mancano il 99,99% di cose da sapere:P) ma di portare alla luce aspetti che molto spesso sono ignorati: dietro a 11 semplici righe di codice a volte si possono nascondere calcoli matematici, studi, ottimizzazioni ed è buona cosa saperlo :)
Esistono una miriade di algoritmi di ordinamento (e con varie proprietà tipo in-place, stabile, ecc…) come ad esempio il Selection Sort (si spazzola ogni volta l’array per trovare il minimo inserendolo nella posizione corretta e rifacendo lo stesso procedimento n volte escludendo la parte già ordinata) che può essere scritto sia in versione iterativa che ricorsiva (dove la chiamata ricorsiva viene fatta su n-1 elementi).
Altri algoritmi di ordinamento possono far uso di strutture dati diverse come heap, alberi di ricerca bilanciati o meno, ecc…
Non esistono sono algoritmi basati sul confronto, ad esempio troviamo il Counting Sort, Radix Sort, Bucket Sort, ecc…
Insomma di algoritmi ne esistono a tonnellate (…di ordinamento, e gli altri? o_O) ognuno diverso dall’altro non solo in termini di complessità ma anche di implementazione e di spazio e sta a noi decidere quale sia il più adatto alle nostre esigenze.

Programmazione orientata agli oggetti

Introduzione

In questi ultimi anni, chi si è appena affacciato al mondo della programmazione, avrà sentito parlare di “programmazione orientata agli oggetti”; con questo termine si intende un modo di programmare che gira attorno al concetto di oggetto, ma cos’è un oggetto?
Un oggetto (object) è un entità che ha un contenuto e alla quale si possono inoltrare delle richieste specifiche al fine di eseguire delle operazioni su di esso.
Un oggetto per dirla in altri termini ha uno stato(dato dall’insieme delle variabili e in generale dal suo contenuto), un comportamento (costituito dai metodi) e un’identità (l’handle ossia quell’indirizzo di memoria univoco per riferirsi all’oggetto in questione).
Noi possiamo immaginare una lampadina come un oggetto che avrà delle proprietà particolari (potenza, colore, ecc…) e delle azioni specifiche (accensione, spegnimento).
Ogni oggetto però non sarà visibile nella sua interezza dagli utilizzatori perchè avrà su di se un’interfaccia che ne faciliterà l’interazione; si può considerare l’interruttore di accensione e spegnimento come un interfaccia che nasconde a noi il vero funzionamento di una lampadina (quindi quanta corrente andrà fatta passare, come funziona internamente, ecc…); tutto ciò permette quindi alla lampadina di essere utilizzata da tutti (sia da chi ha creato la lampadina e sia da chi ignora il suo funzionamento interno).
Dopo aver detto ciò si può capire l’importanza di nascondere l’implementazione perchè l’utilizzatore non si preoccuperà di come è fatta al suo interno la lampadina, ma dovrà solo sapere pochi aspetti relativi all’utilizzo dell’interruttore.
Dopo questa breve panoramica generale, studiamo i vari aspetti della programmazione orientata agli oggetti utilizzando come linguaggio didattico Java.

Classe Lampadina

  1. /**
  2.  * Classe Lampadina.
  3.  *
  4.  * @author Daniele Simonin
  5.  */
  6.  
  7. public class Lampadina {
  8.     private static int numero=0;
  9.     private int potenza=100;
  10.     private String colore="bianco";
  11.     private Boolean acceso=false;
  12.  
  13.     /**
  14.      * Costruttore a 2 parametri
  15.      *
  16.      * @param   potenza potenza della lampadina
  17.      * @param   colore  colore della lampadina
  18.      */
  19.     public Lampadina(int potenza, String colore) {
  20.         numero++;
  21.         this.potenza=potenza;
  22.         this.colore=colore;
  23.     }
  24.  
  25.     /**
  26.      * Costruttore ad un parametro
  27.      *
  28.      * @param   potenza potenza della lampadina
  29.      */
  30.     public Lampadina(int potenza) {
  31.         numero++;
  32.         this.potenza=potenza;
  33.     }
  34.  
  35.     /**
  36.      * Costruttore ad un parametro
  37.      *
  38.      * @param   colore  colore della lampadina
  39.      */
  40.     public Lampadina(String colore) {
  41.         numero++;
  42.         this.colore=colore;
  43.     }
  44.  
  45.     /**
  46.      * Costruttore vuoto
  47.      */
  48.     public Lampadina() {
  49.         numero++;
  50.     }
  51.  
  52.     /**
  53.      * Ritorna il numero totale di lampadine
  54.      *
  55.      * @return  numero totale lampadine
  56.      */
  57.     public static int getNumeroLampadine() {
  58.         return numero;
  59.     }
  60.  
  61.     /**
  62.      * Accende la lampadina
  63.      */
  64.     public void accendi() {
  65.         acceso=true;
  66.     }
  67.  
  68.     /**
  69.      * Spegne la lampadina
  70.      */
  71.     public void spegni() {
  72.         acceso=false;
  73.     }
  74.  
  75.     /**
  76.      * Ritorna lo stato della lampadina
  77.      *
  78.      * @return  stato della lampadina (accesa o spenta)
  79.      */
  80.     public String getStato() {
  81.         return acceso==true?"accesa":"spenta";
  82.     }
  83.  
  84.     /**
  85.      * Setta la potenza della lampadina
  86.      *
  87.      * @param   potenza potenza della lampadina
  88.      */
  89.     public void setPotenza(int potenza) {
  90.         this.potenza=potenza;
  91.     }
  92.  
  93.     /**
  94.      * Ritorna la potenza della lampadina
  95.      *
  96.      * @return  potenza della lampadina
  97.      */
  98.     public int getPotenza() {
  99.         return potenza;
  100.     }
  101.  
  102.     /**
  103.      * Setta il colore della lampadina
  104.      *
  105.      * @param   colore  colore della lampadina
  106.      */
  107.     public void setColore(String colore) {
  108.         this.colore=colore;
  109.     }
  110.  
  111.     /**
  112.      * Ritorna il colore della lampadina
  113.      *
  114.      * @return  colore della lampadina
  115.      */
  116.     public String getColore() {
  117.         return colore;
  118.     }
  119.  
  120.     /**
  121.      * Informazioni sulla lampadina
  122.      */
  123.     public String toString() {
  124.         String info="Potenza: "+getPotenza()+"n";
  125.         info+="Colore: "+getColore()+"n";
  126.         info+="Stato: "+getStato()+"n";
  127.         return info;
  128.     }
  129. }

Incapsulamento

L’incapsulamento sarà ottenuto quando le proprietà dell’oggetto saranno modificabili mediante i metodi che lo stesso oggetto mette a disposizione all’utente (mediante la relativa interfaccia).
La lampadina incapsula a se i fili, resistenze, ecc… e esternalizza solo l’interruttore che permette di agire indirettamente su questi componenti elettrici (e quindi per spegnere la lampadina non dovrò agire sui fili ma sull’interruttore).
Quindi una lampadina con fili scoperti facilmente manomettibili, resistenze variabili esterne, ecc… non soddisferà il principio di incapsulamento perchè chiunque potrà toccare con mano questi componenti (rischiando la fulminazione ;) e bypassare quindi l’utilizzo dell’interruttore.
Nella nostra classe di esempio per accendere la lampadina non agisco direttamente sulla variabile “acceso” ma mediante i metodi accendi() e spegni(), come in questo esempio:

  1. Lampadina lampadina=new Lampadina();
  2. lampadina.acceso=true; // non è possibile farlo
  3. lampadina.accendi();

Occultamento dell’informazione (information hiding)

Questo tipo di occultamento si ottiene rendendo non visibile la struttura interna dell’incapsulamento; quindi riprendendo la metafora con la lampadina non dovremmo far vedere all’utente resistenze, relè che servono solo al funzionamento interno della lampadina e che quindi all’utente non devono interessare.
Questa particolarità della programmazione orientata agli oggetti si ottiene mediante il corretto utilizzo di 3 parole chiave quali:

  • Public: l’elemento di interesse è visibile da chiunque
  • Protected: l’elemento di interesse è visibile solo alle sottoclassi
  • Private: l’elemento di interesse è visibile solo dalla classe stessa

Java inoltre ha un’altra tipologia d’accesso di default che rende l’elemento d’interesse visibile solo a classi appartenenti allo stesso package e viene per questo chiamato package access.

Occultamento dell’implementazione (implementation hiding)

Con questo concetto si vuole tenere nascosto all’utente il vero funzionamento della lampadina.
Nella nostra classe d’esempio si può vedere come l’utente non potrà sapere (ma solo immaginare) che lo stato della lampadina corrisponde ad una variabile booleana (poteva benissimo essere di tipo int oppure una stringa), visto che l’utente accende e spegne la lampadina solo richiamando i metodi corrispondenti.

Conservazione dello stato

La conservazione dello stato è una proprietà fondamentale perchè fa in modo che quando noi stacchiamo l’interruttore dalla lampadina questa rimanga accesa (o spenta).
Lo stato dell’oggetto, costituito da tutti i suoi valori, rimarrà tale durante tutta la vita dello stesso ma verrà perduto quando verrà distrutto l’oggetto.

Identità degli oggetti

L’identità di un oggetto costituisce il suo riferimento e non è altro che un indirizzo di memoria univoco che risiede nello heap (handle).
Potrebbero formarsi delle situazioni pericolose in cui un oggetto rimanga senza alcun riferimento ma nel caso specifico di Java (e di altri linguaggi) la presenza del Garbage Collector ripulisce la memoria di tutti gli oggetti non utilizzati (senza riferimento).

  1. Lampadina lamp1,lamp2;
  2. lamp1=new Lampadina(); // primo oggetto
  3. lamp2=new Lampadina(); // secondo oggetto
  4. lamp1=lamp2;

Nell’esempio vediamo come vada perso il riferimento al secondo oggetto (che quindi non sarà più accessibile e verrà quindi spazzato via).

Concetto di classe e di oggetto

La classe è un modello per entità che hanno in comune determinati aspetti (variabili e metodi), mentre l’oggetto è l’entità generabile da una determinata classe.
Per mettere a fuoco meglio i due concetti possiamo immaginare la categoria dei lavoratori (quindi abbiamo una classe Lavoratore) e ciascun lavoratore che svolge una determinata mansione (ossia oggetti creati dalla stessa classe).
Potremo avere attributi e metodi di due tipi:

  • Di istanza: ossia che vengono duplicati nell’oggetto al momento della sua creazione
  • Di classe: esiste una copia comune a tutte le entità della stessa classe

Principalmente le due tipologie si contraddistinguono dal fatto di avere come keyword Static che rende un metodo o una variabile di classe (altrimenti è d’oggetto).
Nel nostro caso abbiamo un metodo statico utile per sapere il numero totale di lampadina istanziate:

  1. Lampadina lamp1,lamp2;
  2. lamp1=new Lampadina();
  3. lamp2=new Lampadina();
  4. System.out.println(Lampadina.getNumeroLampadine());
  5. System.out.println(lamp1.getNumeroLampadine());

Come potete notare si può richiamare un metodo statico facendo riferimento sia alla classe che all’oggetto (la stessa cosa non vale ovviamente per i metodi o variabili d’istanza).
Una precisazione da fare (giusto per confondere ulteriormente le idee :P), le stesse classi sono oggetti della classe Class e ogni classe eredita dalla classe Object.

Ereditarietà e Composizione

Il vantaggio più evidente nell’addottare questi accorgimenti riguarda la possibilità di riutilizzo delle classi portando quindi ad un risparmio di tempo notevole (sia per quel che riguarda la velocità di aggiornamento).
Creare una lampadina lampeggiante da una lampadina normale risulta più facile che crearla dall’inizio.
Ecco il codice di una pseudo-lampadina lampeggiante:

  1. /**
  2.  * Classe Lampeggiante.
  3.  *
  4.  * @author Daniele Simonin
  5.  */
  6.  
  7. public class Lampeggiante extends Lampadina {
  8.     private int frequenza=10;
  9.  
  10.     /**
  11.      * Costruttore a 3 parametri
  12.      *
  13.      * @param   potenza potenza della lampadina
  14.      * @param   colore  colore della lampadina
  15.      * @param   frequenza   frequenza della lampadina
  16.      */
  17.     public Lampeggiante(int potenza, String colore, int frequenza) {
  18.         super(potenza,colore);
  19.         this.frequenza=frequenza;
  20.     }
  21.  
  22.     /**
  23.      * Costruttore ad un parametro
  24.      *
  25.      * @param   potenza potenza della lampadina
  26.      * @param   frequenza   frequenza della lampadina
  27.      */
  28.     public  Lampeggiante(int potenza, int frequenza) {
  29.         super(potenza);
  30.         this.frequenza=frequenza;
  31.     }
  32.  
  33.     /**
  34.      * Costruttore ad un parametro
  35.      *
  36.      * @param   colore  colore della lampadina
  37.      * @param   frequenza   frequenza della lampadina
  38.      */
  39.     public Lampeggiante(String colore, int frequenza) {
  40.         super(colore);
  41.         this.frequenza=frequenza;
  42.     }
  43.  
  44.     /**
  45.      * Costruttore ad un parametro
  46.      *
  47.      * @param   frequenza  frequenza della lampadina
  48.      */
  49.     public Lampeggiante(int frequenza) {
  50.         this.frequenza=frequenza;
  51.     }
  52.  
  53.     /**
  54.      * Costruttore vuoto
  55.      */
  56.     public Lampeggiante() {}
  57.  
  58.     /**
  59.      * Setta la frequenza della lampadina
  60.      *
  61.      * @param   frequenza frequenza della lampadina
  62.      */
  63.     public void setFrequenza(int frequenza) {
  64.         this.frequenza=frequenza;
  65.     }
  66.  
  67.     /**
  68.      * Ritorna la frequenza della lampadina
  69.      *
  70.      * @return  frequenza della lampadina
  71.      */
  72.     public int getFrequenza() {
  73.         return frequenza;
  74.     }
  75.  
  76.     /**
  77.      * Informazioni sulla lampadina
  78.      */
  79.     public String toString() {
  80.         String info=super.toString();
  81.         info+="Frequenza: "+getFrequenza()+"n";
  82.         return info;
  83.     }
  84.  
  85. }

L’ereditarietà permette di utilizzare, così come sono, metodi e variabili (se non private) delle sovraclassi (in questo caso della sovraclasse Lampadina).
I metodi ereditati nel nostro caso sono:

  • getNumeroLampadine()
  • accendi()
  • spegni()
  • getStato()
  • setPotenza()
  • getPotenza()
  • setColore()
  • getColore()

Quando estendiamo una classe abbiamo la possibilità di sovrascrivere determinate metodi, come abbiamo fatto nel metodo toString() che richiama il metodo omonimo della sovraclasse per poi aggiungerci l’informazione riguardante la frequenza (ottenendo quindi due comportamenti diversi per ciascuna delle classi).
Oltre a poter ereditare e sovrascrivere metodi possiamo aggiungerne di nuovi e quindi di fatto estendere la sovraclasse.
I metodi aggiunti sono:

  • setFrequenza()
  • getFrequenza()

La lampadina lampeggiante quindi avrà il “parametro frequenza” aggiunto, il metodo toString() sovrascritto (in modo da includere l’informazione relativa alla frequenza) e tutti gli altri metodi ereditati da una normale lampadina (quali potenza e colore).
Oltre all’ereditarietà appena descritta esiste anche la composizione, nella quale all’interno di una particolare classe si creando ed utilizzano altri oggetti.
Per spiegare meglio il concetto possiamo pensare il volto di un umano come una classe, nella quale al suo interno ci saranno oggetti istanziati dalle classi Occhio, Bocca, Naso, ecc… si può quindi notare che non vi è alcuna ereditarietà (un occhio non ha niente in comune con il volto, ecc…) ma solo un utilizzo di oggetti distinti all’interno di una o più classi.
Comunemente entrambi i metodi vengono utilizzati insieme, con la differenza che la composizione viene usata quando si vogliono utilizzare i metodi e le proprietà di una classe all’interno di un’altra senza la sua interfaccia (settando eventualmente come private gli oggetti istanziati all’interno della classe).

Polimorfismo

Il polimorfismo consiste nel cambio di comportamento di uno stesso metodo a seconda dell’oggetto a cui ci si riferisce (detta in parole povere), e questo risulta essere possibile principalmente grazie al late binding.
Il late binding è una proprietà di Java (e di altri linguaggi) che permette di conoscere il tipo di un oggetto in tempo di esecuzione, richiamando il metodo adeguato.
Ecco un esempio:

  1. Boolean prova=false; // o meglio ancora un valore in input dall'utente
  2. Lampadina a,b,illuminazione;
  3. a=new Lampadina();
  4. b=new Lampeggiante();
  5. illuminazione=(prova==true)?a:b;
  6. System.out.println(illuminazione);

Possiamo vedere che il legame tra l’identificatore “illuminazione” e i vari oggetti (“a” e “b”) è stabilito in tempo di esecuzione e non di compilazione, chiamando il metodo toString() adeguato.
Possiamo notare che una forma similare di “adattamento” (che non ha niente a che fare con il polimorfismo) coinvolge anche il costruttore (e in generale tutti i metodi) che è presente più volte all’interno della classe ma sempre con segnatura (che contraddistingue numero e ordine dei parametri) diversa.
Quando creeremo un oggetto, a seconda dei parametri passati, verrà richiamato il costruttore opportuno.

Conclusione

In questo tutorial ho voluto dare una panoramica generale della programmazione orientata agli oggetti quindi alcuni aspetti sono stati volutamente ignorati o comunque non approfonditi, tutto ciò per essere il più possibile indipendente dal linguaggio di programmazione utilizzato (ad esempio non ho parlato delle classi astratte, interfacce e delle classi parametriche).
Ovviamente linguaggi di programmazione diversi differiscono per alcuni aspetti, ad esempio Actionscript 2 non prevede l’overloading (sovraccarico) del costruttore, ecc…
Insomma questo tutorial da solo un’introduzione all’argomento che potete approfondire grazie al nostro amico Google ;)

Strutture di controllo

Requisiti

  • Malleabilità mentale :)
  • 10 minuti di tempo disponibili :)
  • Conoscenze minime di programmazione (se conoscete un termine più riduttivo di minimo, sostituitelo pure ;)

Introduzione

Quello che stò per scrivere è una panoramica generale comune a tutti i linguaggi di programmazione (tipo ActionScript, PHP, Java, ecc…); in questo caso l’argomento verrà trattato con esempi scritti in ActionScript ( che si può considerare il nipotino di Java ;).

If…Else (e operatori condizionali)

Questa istruzione di selezione è la più “utilizzata” di tutte visto che ci permette di cambiare “percorso” al nostro programma ( e quindi di renderlo “ramificato”).

  1. condizione=true;
  2. if (condizione) {
  3.     trace("La condizione è vera");
  4. } else {
  5.     trace("La condizione è falsa");
  6. }

Come potete notare questo ci permette di esaminare la condizione e di comportarci di conseguenza. Infatti se la condizione è vera, nella nostra finestra di debug comparirà la scritta “La condizione è vera” (è il nostro caso) altrimenti se la condizione è falsa comparirà “La condizione è falsa”.
Un applicazione utile può essere quella di mettere in una variabile il massimo valore di due variabili (supponiamo che i due valori siano diversi), ad esempio:

  1. numero1=10;
  2. numero2=5;
  3. if (numero1>numero2) {
  4.     max=numero1;
  5. } else {
  6.     max=numero2;
  7. }

Come possiamo notare la variabile max conterrà la variabile più grande, quindi il massimo tra numero1 e numero2. Ovviamente possiamo annidare più if…else a nostra discrezione. Un altro modo per eseguire la stessa operazione dello scriptino di prima stà nell’usare un operatore condizionale di questo tipo:

  1. numero1=10;
  2. numero2=5;
  3. max=numero1>numero2?(numero1):(numero2);

Ovviamente la possibilità di scelta tra questi due metodi stà a voi (in questa applicazione io preferisco usare l’operatore condizionale).
Come ho accennato prima si possono annidare più if o else if, questo per algoritmi leggermente più “contorti”. Volendo migliorare il programma di prima (che non funzionava correttamente se i due numeri erano uguali) possiamo scrivere il seguente codice:

  1. numero1=10;
  2. numero2=5;
  3. if (numero1==numero2) {
  4.     trace("I due numeri sono uguali");
  5. } else if (numero1>numero2) {
  6.     max=numero1;
  7. } else {
  8.     max=numero2;
  9. }

La stessa cosa poteva essere fatta con due operatori condizionali annidati, ma in questo caso la cosa non è molto conveniente sia per la comprensione del codice e sia per la difficoltà di annidare due operatori condizionali (con quei punti di domanda e i due punti alla fine si fa solo confusione :P), quindi consiglio di usarli solo nella applicazioni semplici (tipo calcolare il max tra due variabili).
Nella condizione hanno molta importanza gli operatori logici quali l’AND ( simbolo &&), l’OR ( simbolo || ) e il NOT ( simbolo ! ) che diventano utili quando nella condizione devono essere soddisfatte più cose (ad esempio che una variabile sia uguale a 10 e l’altra a 5, in questo caso si userà l’AND).

Switch…Case…Default

Questo blocco di istruzioni possono essere pensate come tanti if di seguito. Facciamo subito un esempio (che alla fine è alla base di tutto ;):

  1. switch (numero) {
  2.     case 1:
  3.         trace ("il numero è Uno");
  4.         break;
  5.     case 2:
  6.         trace ("il numero è Due");
  7.         break;
  8.     case 3:
  9.         trace ("il numero è Tre");
  10.         break;
  11.     default:
  12.         trace ("il numero non è nè Uno, Due, Tre")
  13. }

Questo blocco si potrebbe fare anche con una serie di if annidati:

  1. if (numero==1) trace("il numero è Uno");
  2. else if (numero==2) trace ("il numero è Due");
  3. else if (numero==3) trace("il numero è Tre");
  4. else trace("il numero non è nè Uno, Due, Tre");

In questo caso lo switch…case…default risulta più comprensibile e facilmente modificabile. L’istruzione break serve per uscire dall’istruzione di controllo (quindi si esce dallo switch). Ipotizzando che il numero sia 1, senza il break il programma controllerebbe pure le altre uguaglianze, mentre trovandosi un break esce dallo switch (velocizzando l’esecuzione del programma). Ci sono dei casi in cui il break può anche non essere utilizzato, ad esempio quando dobbiamo trattare più casi allo stesso modo.

Do…While e While

Queste istruzioni ho deciso di trattarle insieme visto che si differenziano solo per una piccola cosetta (non meno importante). Facciamo subito un esempio si While:

i=0;
while (i< =10) { trace(i++); }[/as] Questo piccolo frammento di codice conta i numeri dallo 0 al 10. Come potete vedere c'è una fase in cui inizializzo la variabile "i" e una in cui la incremento finchè è minore uguali a 10. Questo ciclo, appunto, fa assumere alla variabile "i" tutti i numeri interi tra 0 e 10 (inclusi). Se volessimo fare la stessa cosa con il Do...while faremo così: [as]i=0 do { trace(i++) } while (i<=10);[/as] Voi direte, ma praticamente sono due sintassi diverse dello stesso comando? E io vi rispondo : certo che no... adesso vi spiego il perchè. La differenza tra le due istruzioni stà nel fatto che nel Do...While la condizione viene controllata solo dopo l'esecuzione dell'istruzione (che di conseguenza viene eseguita almeno una volta), mentre nel While il discorso si inverte (ma và? :) Un utile applicazione del Do...While in Java e Co. stà nel controllo del input (da tastiera) dell'utente, tutto questo perchè l'input deve essere richiesto almeno una volta (e poi controllato). For…To

Questa è l’istruzione che io uso di più (mi stà più simpatica ed è molto più corta da scrivere ehehe) e ha lo stesso scopo delle altre due precedenti istruzioni, ossia quello di eseguire un ciclo finchè una condizione si è verificata.

for (var i=0;i< =10;i++) trace(i);[/as] Come potete notare in un unica riga ho inizializzato la variabile , ho messo la condizione, incrementato la "i" e visualizzata. L'unica attenzione stà nel fatto di non modificare la variabile "i" durante il ciclo, perchè molto probabilmente andremo ad incorrere su un ciclo infinito (che traducendo in termini pratici...si pianta tutto ehehe ;) Per accennare la funzione dell'istruzione continue facciamo un altro esempio: [as]for (var i=0;i<=10;i++) { if (i==5) continue; trace(i); }[/as] Come possiamo notare se "i" è uguale a 5 eseguiamo l'istruzione continue. Questo ci permette di uscire dall'iterazione corrispondente ma non dal ciclo (e di conseguenza si salta il conteggio di 5 facendolo poi riprendere dal 6). Se sostituiamo il continue con il break, faremo in modo che il conteggio si blocchi al numero 5 (visualizzando i numero 0 1 2 3 4). Volendo si può fare a meno di queste istruzioni e modificare opportunamente la condizione, ad esempio: [as]for (var i=0;i<=10;i++) { if (i!=5) trace(i); }[/as] Otterremo lo stesso risultato dello script precedente (quindi abbiamo sostituito il continue). Mentre se volessimo sostituire il break faremo così: [as]for (var i=0;i<=10 && i!=5;i++) { trace(i); }[/as] Come potete notare lo script che ho appena scritto non è molto "intelligente" infatti basterebbe mettere come condizione i<5, ma visto che gli esempi servono per capire... :) Tutte le cose belle finiscono prima o poi

Bene eccoci giunti alla fine del tutorial. Come avete notato le nozioni non sono molto complicate e essendo correlate da dei semplici esempi si trasformano in nozioni quasi banali. Ovviamente saper programmare non significa sapere queste cose, ma saperle applicare nel modo più corretto ed efficace possibile.
Inoltre nel caso dell’ActionScript occorre sapere pure altre istruzioni (gotoAndPlay(),LoadVariable(),XML,ecc…) per poter fare programmi che abbiano una certa utilità. La stessa cosa vale per Java dove occorre per forza sapere le API( per fare interfacce grafiche, leggere dati su disco, interfacciarsi a protocolli di rete, ecc…).