Julia



Controllo di flusso          

La parte relativa al controlo del flusso delle istruzioni in un programma è argomento forse un po' tedioso ma tuttavia necessario e importante in ogni linguaggio di programmazione, a meno che non stiate avvicinandovi a Brainfuck o a qualche altro esoterico simile :-).

Iniziamo subito dalla classica istruzione if.
Le parole coinvolte sono in realtà 3, ovvero; if, elseif e else con la keyword end che funge da terminatore; e il formato di queste può essere visto in 3 modi diversi, dal momento che solo if e end sono sempre necessari, elseif ed else possono essere opzionali:
 
if condizione booleana
istruzioni
end


oppure

if condizione booleana
istruzioni
else
istruzioni
end


e infine

if condizione booleana
istruzioni
elseif condizione booleana
istruzioni
else
istruzioni
end


Una "condizione booleana" (che al contrario di quanto avviene in altri linguaggio non è necessario sia messa all'interno delle parentesi tonde) è in pratica una istruzione che può assumere solo due valori cioè vero o falso. Un rapido esempio che riporta l'ultimo dei 3 casi esposti è il seguente:

  Esempio 4.1
1
2
3
4
5
6
7
8
9
10
print("Inserisci un numero: ")
s1 = readline(STDIN)
n1 = int(s1)
if n1 > 0
print("Inserito numero maggiore di 0")
elseif n1 < 0
print("Inserito numero < 0")
else
print ("Proprio 0?")
end

Lascio a chi legge di inventarsi esempi con un numero diverso di posibilità. Va da sé che i rami che iniziano con elseif possono essere in numero a piacere se le possibilità da esaminare sono numerose (anche se, in pratica, in un programma ben organizzato non dorebbe succedere praticamente mai). Invece if ed else possono comparire una sola volta.
La condizione deve essere esclusivamente booleana cioè deve avere avere valore true o false. Non è ammesso ad esempio scrivere cose come:

if 1
print("ciao")
end


nel caso ci provaste l'interprete indica chiaramente il suo pensiero:

ERROR: type: non-boolean (Int64) used in boolean context

Se al numero 1 nel codice precedente sostituite true la cosa funziona e viene stampata la stringa, se mettete false la cosa funziona e non viene stampato nulla.
Ovviamente una volta eseguito il codice presente in una delle ramificazioni tutte le altre vengono escluse. Inoltre le epsressioni interne ai branch la cui condizione non si realizza non vengono elaborate in alcun modo, com'è logico che sia, in fondo. Per quanto banale come consiglio è bene ricordare che bisogna fare attenzione a scegliere con cura le condizioni... in fondo, il cuore del nostro "if" sta proprio in quello. Sarebbero, come accennato, da evitare sequenza troppo lunghe o livelli di if annidati troppo profondi; questo non perchè le cose non funzionino ma per problemi di leggibilità e manutenibilità del programma.

Qualora invece abbiamo casi molto ristretti, diciamo una sequenza if - else pura e semplice possiamo ricorrere ad una scrittura semplificata, o meglio più compatta attraverso l'operatore ? : (punto interrogativo + due punti). Il suo funzionamento è il seguente:

espressione boleana ? istruzione-1 : istruzione-2

ovvero: se si verifica la condizione booleana viene eseguita l'istruzione 1 altrimenti la 2. Quindi:

  Esempio 4.2
1
2
3
4
print("Inserisci un numero: ")
s1 = readline(STDIN)
n1 = int(s1)
print(n1 >= 0 ? "numero positivo" : "numero negativo")

A voi giudicare se la compattezza e la sinteticità della riga 4 siano un buon compenso a fronte della leggibilità della sua equivalente "normale":

if n >= 0
print "numero positivo"
else
print "numero negativo"
end


a mio avviso si, ma è un'opinione...... La cosa comunque si può ulteriormente ampliare se volete includere anche la possibile triplette if - elseif - else:

  Esempio 4.3
1
2
3
4
print("Inserisci un numero: ")
s1 = readline(STDIN)
n1 = int(s1)
print(n1 > 0 ? "numero positivo" : n1 < 0? "numero negativo" : "proprio 0" )

A voi stabilire se vi piace anche così o se preferite la versione più verbosa. Questa osservazione vale in particolare se costruire delle lunghe catene con questo operatore ternario.

Julia prevede due istruzioni specifiche per la realizzazione di iterazioni: while e for.
Chi è pratico di altri linguaggi non troverà nulla di particolarmente nuovo in esse.

La prima, while è molto semplice, come dimostra la sua definizione formale:

while (condizione booleana)
istruzioni
end

Viene valutata, all'ingresso la condizione booleana: se è vera vengono eseguite le istruzioni altrimenti no. Ancora più facile provando l'esempio:

  Esempio 4.4
1
2
3
4
5
6
7
print("Inserisci un numero: ")
s1 = readline(STDIN)
n1 = int(s1)
while n1 < 20
println(n1)
n1 = n1 + 1
end

Se immettiamo una valore minore di 20 la riga 5 e poi la 6 vengono eseguite. Da 20 in su non se ne fa niente. Importante controllare la condizione di uscita e che possa venire realizzata; se mancasse la riga 6 ed immettessimo una vlore minore di 20 in ciclo non terminerebbe mai. Come nel caso dell'istruzione if anche qui la condizione deve essere ture o false; non sono ammessi altri valori. Questo è un suggerimento per realizzare in modo elegante un loop infinito, se vi servisse davvero; l'istruzione potrebbe essere:

while true
istruzioni
end


ovviamente dovete sapere quel che state facendo....

Anche il loop introdotto da for è concettualmente molto semplice:

for range
istruzioni
end


Bisogna capire come lavorare su quel range, tenendo presente che dei range parleremo meglio più avanti:

for x = 1:5
println("ciao")
end


questo frammento di programma stampa per 5 volte la stringa "ciao". Una importante osservazione riguarda proprio la variabile x introdotta nella prima riga: essa esiste solo ed esclusivamente nell'area di interesse del ciclo for, al di sopra ed al di sotto non viene riconosciuta e risulta come una variabile non definita. Come in altri linguaggi è anche possibile stabilire uno step diverso da uno in caso di attraversamento di un range numerico; in questo caso la sintassi noon è, a mio avviso la più limpida e chiara possibile, infatti il nuovo passo viene inserito tra i due estremi del range:

julia> for x in 0:2:10
println(x)
end
0
2
4
6
8
10

In questo piccolo esempio il due evidenziato è il nuovo output, come si vede anche dall'output. Francamente non mi piace molto ma è così.....

Un sistema alternativo di definizione di un range è il seguente:

julia> for x in [1,3,5,7]
println (x)
end
1
3
5
7

Come si vede  il range è costituito da un insieme e non da una sequenza continua di valori. Ma non è mica necessario usare solo i numeri:

for x in ["hello ", "world", "!"]
print(x)
end

Indovinate cose ne esce....

Come in altri linguaggi è possibile usare più variabili per gestire il for ed anche innestarne uno dentro l'altro:

  Esempio 4.5
1
2
3
4
5
6
7
8
9
10
11
for x = 1:3, y = 1:4
println(x + y)
end

println()

for x = 1:3
  for y = 1:4
  println(x + y)
  end
end

I due cicli, quello dalla riga 1 alla 3 e quello dalla 7 alla 11, danno lo stesso output. Il println alla 5 serve naturalmente solo ma separare visivamente gli output.

Due istruzioni utili in alcuni casi in congiunzione con for e while sono le seguenti:

break: interrompe il ciclo e restituisce il controllo alle istruzioni di livello superiore
continue: salta l'iterazione corrente e passa a quella successiva

risolviamo la cosa con due esempi:

  Esempio 4.6
1
2
3
4
5
6
for x = 1:10
println(x)
  if x == 5
  break
  end
end

L'output è il seguente:

1
2
3
4
5

ovvero i numeri da 1 a 5. L'istruzione alla riga 3 manda in esecuzione il break che,come detto, interrompe il ciclo e restituisce il controllo al livello superiore. Nel caso specifico, siamo già a top level, non c'è nulla che contenga il for se non il programma stesso che quindi termina. Quindi break è utile anche per interrompere eventuali loop infiniti.

  Esempio 4.7
1
2
3
4
5
6
for x = 1:10
  if x == 5
  continue
  end
println(x)
end

Che espone a video:

1
2
3
4
6
7
8
9
10

I più attenti avranno notato che manca il numero 5. Infatti alla riga 2 viene permessa l'esecuzione di continue che interrompe l'iterazione in atto immediatamente, quindi prima che venga eseguita l'operazione di scrittura alla riga 5, e si passa alla successiva.

Un caso un po' particolare sono le cosiddette compound expressions. Si tratta di blocchi di espressioni che vanno valutate non "una per una" ma nel loro complesso. Possono venire utili in alcuni casi e sono caratterizzate dal fatto di trovarsi all'interno di una coppia begin - end. Vediamo un esempio:

julia> z = begin
x0 = 1
x1 = 5
x2 = x0 + x1
end
6

julia> z + 1
7

a z è associata quindi una compund expression e con il valore risultante può essere usato nel resto del programma.