Octave: Acceso a Vettori e Cicli for
Il nostro obiettivo è formulare algoritmi, giusto?
Proviamo a capire se possiamo farlo
con i costrutti visti finora
Proviamo a calcolare la somma degli elementi di un vettore
Come fare?
■ La somma è inizialmente uguale a 0
■ Consideriamo tutti gli elementi successivi
■ Aggiungiamoli uno ad uno alla somma
Abbiamo gli strumenti per codificarlo?
Proviamo a calcolare la somma degli elementi di un vettore
■ La somma è inizialmente uguale a 0
s = 0;
■ Consideriamo tutti gli elementi successivi
??? % Non sappiamo "spostarci" su un vettore
■ Aggiungiamoli uno ad uno alla somma
s = s + ??? % E neanche accedere ad un elemento
Ancora non ci siamo: vediamo di rimediare...
Abbiamo visto come costruire un vettore in Octave:
[1, 3, 5]
Quando si implementano algoritmi può essere utile:
■ Accedere ad un elemento specifico...
■ ...Per "leggerlo" o "modificarlo"
In Octave lo si può fare utilizzando la notazione:
<vettore>(<espressione>)
■ <vettore>è una espressione che denota un vettore
■ <espressione>è deve denotare un indice (un intero)
__Gli elementi del vettore (o celle dell'array) sono numerate[]: # Elementi
di Informatica e
Applicazioni Numeriche T
Progettazione di Algoritmi:
Approccio Top-Down e Bottom Up:__
■ Il primo indice è sempre 1
■ L'ultimo indice è uguale al numero di elementi
Per ottenere il numero di elementi si può usare la funzione:
length(<array>)
■ Attenzione: lengthfa cose diverse se invoca su altri tipi di dato
Gli elementi del vettore (o celle dell'array) sono numerate:
■ Il primo indice è sempre 1
■ L'ultimo indice è uguale al numero di elementi
In alternativa, si può accedere direttamente all'ultimo elemento con:
<array>(end)
■ endfunziona solo se usato come indice
Gli elementi del vettore vengono trattati come variabili
Supponiamo di avere:
x = [1,2,3,5,6,7]
Possiamo ottenere il valore di un element con:
x(4) % denota il valore 5
E modificare un elemento con:
x(4) = 2 % assegna il valore 2 alla 4°cella
Sempre assumendo di avere:
x = [1,2,3,5,6,7]
Se usiamo un indice < 1, otteniamo un errore:
x(0) % errore
x(0) = 2 % errore
Se usiamo un indice > length(x), invece:
x(9) % ancora un errore
x(9) = 1 % Otteniamo x = [1,2,3,5,6,7,0,0,1]
■ Il vettore viene espanso, riempiendo con 0le celle intermedie
Il simbolo []denota un vettore/matrice vuoto
Possiamo definire un vettore vuoto:
x = [] % x è vuoto, length(x) è 0
Possiamo estendere un vettore vuoto:
x(3) = 10 % otteniamo x = [0, 0, 10]
Possiamo cancellare un elemento assegnandovi []:
x(2) = [] % otteniamo x = [0, 10]
L'estensione e []consentono di manipolare la lunghezza
Vediamo come usare queste informazioni per il nostro algoritmo
■ Supponiamo che il nostro vettore si chiami v
v = % il nostro vettore
s = 0;
<facciamo variare una variabile ii da 1 a length(v)>
s = s + v(ii)
■ Quando iiavrà raggiunto il valore length(v)...
■ ...la variabile sconterrà la somma
Ci rimane da capire come gestire l'indice ii
for
Octave ci permette di farlo utilizzando una istruzione
■ Istruzione = unità esecutiva elementare di un programma
■ Un programma è in prima battuta una sequenza di istruzioni
Un esempio:
■ Espressione + [invio] = una istruzione
Vediamo ora un nuovo tipo di istruzione:
Istruzioni di iterazione o "cicli": permettono
di eseguire ripetutamente una porzione di codice
for
La nostra prima istruzione di iterazione si chiama "ciclo for"
Sintassi:
for <variabile> = <vettore>
<corpo>
end % o anche endfor (incompatibile con matlab)
■ <corpo>è una sequenza di istruzioni
■ Il corpo viene ripetuto per ogni elemento di <vettore>
Ad ogni ripetizione (i.e. iterazione):
■ <variabile>assume il valore di un elemento di vettore
Nota: il simbolo "=" in questo caso non ha il solito significato...
for
Un esempio semplice:
for v = [2, 4, 6]
v % denota il valore di "v"
end
Quando inserite una istruzione fornella finestra dei comandi:
■ Octave non esegue subito l'istruzione
■ Vi permette di continuare a scrivere e premere [invio]
■ Finché non inserite la parola chiave end
Solo allora l'istruzione forè completa (e quindi eseguibile)
for
Il ciclo forci permette di codificare il nostro algoritmo:
Supponendo che il nostro vettore si chiami v
s = 0;
for ii = 1:length(v)
s = s + v(ii)
end
Oppure, in modo ancora più semplice:
s = 0;
for w = v % w assume uno per uno i valori di v
s=s+w
end
La funzione predefinita sumin Octave fa (più o meno) questo
■ Octave offre delle modalità avanzate di accesso ai vettori
■ In alcuni casi fanno molto comodo
Adesso ne vediamo velocemente una "carrellata"
Si può usare un vettore per accedere ad elementi multipli:
La sintassi è:
<vettore>(<vettore di indici>)
Per esempio:
x = [2, 4, 6, 8];
ii = [2, 3]; % "ii" per evitare di rendere
% inaccessibile l'unità immaginaria
x(ii) % denota [4, 6]
■ Il risultato è un nuovo vettore
■ Contiene gli elementi di x, agli indici specificati da ii
Supponiamo di voler sommare le celle adiacenti di:
a = [1, 2, 3, 4, 5]
Possiamo usare:
a(1:end-1) + a(2:end)
■ a(1:end-1)denota [1, 2, 3, 4]
■ a(2:end)denota [2, 3, 4, 5]
Il risultato è:
[3, 5, 7, 9] % [1, 2, 3, 4] + [2, 3, 4, 5]
Oppure, supponiamo di voler sommare le celle pari e dispari:
a = [1, 2, 3, 4, 5, 6]
Possiamo usare:
a(1:2:end) + a(2:2:end)
■ a(1:2:end)denota [1, 3, 5]
■ a(2:2:end)denota [2, 4, 6]
Il risultato è:
[3, 7, 11]
L'accesso mediante vettori funziona anche in assegnamento!
Sintassi:
<vettore>(<vettore di indici>) = <vettore>
Per esempio:
x = [2, 4, 6, 8];
ii = [2, 3];
x(ii) = [10, 11] % otteniamo x = [2, 10, 11, 8]
Se le dimensioni non combaciano, Octave fa broadcasting:
x(ii) = 0 % otteniamo x = [2, 0, 0, 8]
■ È utile per fare un assegnamento "in massa"
Octave: Acceso a Matrici
e Cicli Innestati
Alziamo un po' il livello di difficoltà
Supponiamo di voler sommare gli elementi di una matrice
Un possibile algoritmo:
M = % la nostra matrice
s=0
<per ogni elemento della matrice>
s = s + <un elemento della matrice>
Come iterare sugli elementi di una matrice?
■ Potremmo utilizzare un ciclo for...
Vediamo cosa succede iterando con un forsu una matrice:
M = [1, 2, 3; 4, 5, 6; 7, 8, 9]; % [1, 2, 3]
% [4, 5, 6]
% [7, 8, 9]
for w = M
w % giusto per stampare w
end
La variabile wassume i valori:
■ [1; 4; 7], poi [2; 5; 8], poi [3; 6; 9]
Ossia le colonne di M!
Come possiamo fare per iterare sugli elementi?
Una soluzione semplice: usare due cicli for
M = [1, 2, 3; 4, 5, 6; 7, 8, 9];
for c = M % itero sulle colonne
for v = c % poi sugli elementi della colonna
v
end % fine ciclo interno
end % fine ciclo esterno
■ forè una istruzione
■ Quindi il corpo di un forpuò contenere un altro for!
I due cicli si dicono innestati
■ endsi riferisce sempre all'ultimo fornon ancora chiuso
Ecco quindi un possibile codice per il nostro algoritmo:
M = % la nostra matrice
s=0
for c = M
for v = c
s=s+v
end
end
Notate come il corpo di ogni forè leggermente indentato?
■ Non è obbligatorio, ma rende il codice più leggibile
In alternativa, possiamo usare la funzione sum
Ci vuole però qualche accortezza:
M = [1, 2; 3, 4] % [1, 2]
% [3, 4]
sum(M) % denota [4, 6]
■ Se applicata ad una matrice...
■ ...sumcalcola la somma colonna per colonna
Per avere la somma di tutti gli elementi:
sum(sum(M)) % denota 10
■ La seconda applicazione di sumottiene la somma totale
E se volessi iterare riga per riga?
M = % la nostra matrice
s=0
for ii = 1:<numero di righe>
for jj = 1:<numero di colonne>
s = s + <elemento in posizione i,j>
end
end
Occorre un modo per:
■ Conoscere la dimensione della matrice (righe/colonne)
■ Accedere ad un elemento arbitrario
Accedere ad elemento arbitrario di una matrice è facile:
Sintassi:
<matrice>(<indice riga>,<indice colonna>)
Per esempio:
a = [2, 4; 6, 8]; % corrisponde a [2, 4]
%
[6, 8]
a(1,2) % denota 4
a(2,2) % denota 8
a(1, 1) = 0 % assegna 0 nella posizione 1,1
Per ottenere le dimensioni di una matrice invece si usa:
rows(<matrice>) % denota il numero di righe
columns(<matrice>) % denota il numero di colonne
size(<matrice>) % denota entrambi, in un vettore
Qualche esempio:
a = [2, 4; 6, 8]; % corrisponde a [2, 4]
%
[6, 8]
rows(a) % denota 2
columns(a) % denota 2
size(a) % denota [2, 2]
Qualche osservazione interessante:
a = [2, 4; 6, 8];
b = [2, 4];
c = 2;
size(a) % denota [2, 2]
size(b) % denota [1, 2]
size(c) % denota [1, 1]
■ Octave tratta vettori e scalari come particolari matrici!
■ In realtà, tutti e tre sono considerati array multidimensionali
Per noi, parlare di scalari/vettori/matrici sarà sufficiente
■ Esistono modalità di accesso avanzate anche per le matrici
■ Di nuovo, possono fare molto comodo
Vale la pena di darci un'occhiata
Se vogliamo accedere ad una sola riga/colonna:
Usiamo il simbolo :al posto dell'indice non specificato
<matrice>(2,:) % accede alla seconda riga
<matrice>(:,2) % accede alla seconda colonna
■ La notazione è identica a quella dei range...
■ ...Mancano i due "estremi", però...
■ ...Ed il significato è proprio diverso
Si può cancellare una una riga/colonna assegnandovi []
<matrice>(1,:) = [] % cancella la prima riga
Qualche esempio:
a = [2, 4; 6, 8]; % corrisponde a [2, 4]
%
[6, 8]
a(1,:) % denota [2, 4]
a(:,1) % denota [4; 8]
a(1,:) = [1, 1] % otteniamo [1, 1]
%
[6, 8]
a(:,2) = [0; 0] % otteniamo [1, 0]
%
[6, 0]
a(1,:) = [] % cancella la prima riga,
% otteniamo [6, 0]
Per accedere a righe/colonne multiple, usiamo vettori di indici
Sintassi:
<matrice>(<vettore righe>, <vettore colonne>)
Qualche esempio:
a = [1, 2, 3; 4, 5, 6; 7, 8, 9]; % [1, 2, 3]
% [4, 5, 6]
% [7, 8, 9]
a([1,3], :) % denota % [1, 2, 3]
%
[7, 8, 9]
a([1,3], [1,3]) % denota % [1, 3]
%
[7, 9]
Se accediamo ad una matrice con un indice unico:
■ Viene trattata come se fosse un vettore
■ Con le colonne disposte in sequenza
a
(c
b
d)
⟷
(a
c
b
d)
Permette di accedere ad un gruppo arbitrario di elementi:
a = [2, 4; 6, 8]; % [2, 4]
% [6, 8]
a(1) % denota 2
a(2) % denota 6
a(3) % denota 4
a(4) % denota 8
Una conseguenza importante:
La notazione:
<matrice>(:)
Restituisce tutti gli elementi, in forma di vettore
Possiamo usarla per calcolare la somma:
M = [1, 2; 3, 4];
V = M(:) % ottiene V = [1, 3, 2, 4]
sum(V)
Oppure direttamente sum(M(:))
Octave: Valori Logici
ed Istruzioni Condizionali
Supponiamo di voler calcolare il massimo di un vettore
Cosa riusciamo a scrivere con le istruzioni che conosciamo?
V = % il nostro vettore
y = V(1) % primo tentativo: max = il primo elemento
for ii = 2:length(V) % iteriamo sul resto del vettore
<se V(ii) è maggiore di y, allora y = V(ii)>
end
Abbiamo bisogno di un costrutto che:
■ Permetta di stabilire se V(ii)è maggiore di y
■ Se questo è vero, esegua l'istruzione y = V(ii)
Questa funzionalità è fornita dalle istruzioni condizionali
if
Noi utilizzare una sola istruzione condizionale, chiamata if
Sintassi:
if <espressione>
<corpo1>
else % opzionale
<corpo2> % opzionale
end % oppure endif (incompatibile con matlab)
■
■
■
■
<espressione>può denotare "vero" o "falso"
<corpo1>e <corpo2>sono sequenze di istruzioni
<corpo1>esegue se <espressione>denota il valore "vero"
<corpo2>esegue se <espressione>denota il valore "falso"
Ma cosa vuol dire "denotare vero o falso"?
Octave infatti supporta anche valori logici:
Sintassi:
true % vero
false % falso
Vengono restituiti dagli operatori di confronto:
Sintassi e semantica:
<dato> == <dato> % "true" se i dati sono uguali
<dato> ~= <dato> % "true" si i dati sono diversi
<dato> != <dato> % come ~=, incompatibile con matlab
<, <=, >, >= % fanno quello che vi aspettate :-)
Servono a verificare se due dati soddisfino una condizione
Qualche esempio:
c = 2;
c < 3 % denota true
c ~= 2 % denota false
Nel nostro algoritmo:
V = % il nostro vettore
y = V(1)
for ii = 2:length(V)
if V(ii) > y
y = V(ii)
end
end
■ L'indentazione non è necessaria, ma è consigliata
Per esprimere condizioni complesse usiamo gli operatori logici
Sintassi e semantica:
<dato> & <dato> % true sse i due dati sono true
<dato> | <dato> % true sse almeno un dato è true
~ <dato> % true sse il dato è falso
Qualche esempio:
(x >= 3) & (x < 5) % true se x è tra 3 e 5
(x == 0) | (x > 2)
■ Esistono anche con forma/semantica diversa: &&, ||...
■ ...Ma non la considereremo in questo corso
I valori logici sono assimilabili ai numeri 1("vero") e 0("falso")
■ Si comportano come numeri se usati per fare calcoli
■ È possibile usare numeri al posto di valori logici
■ 0corrisponde a "falso"
■ Qualunque numero diverso da 0corrisponde a vero
Un errore da manuale:
if x = 5 % l'operatore di uguaglianza è "=="!
y=y+1
end
■ x = 5denota 5, che corrisponde a true!
Gli operatori di confronto e logici:
Se applicati a vettori o matrici, operano elemento per elemento
a = [1, 2; 3, 4]; % [1, 2]
% [3, 4]
b = [1, 2, 3];
a < 3 % denota [1, 1]
%
[0, 0]
(b > 1) & (b < 3) % [0, 1, 1] & [1, 1, 0]
% quindi [0, 1, 0]
■ È utile per verificare molte condizioni in un colpo solo
■ In questo caso, può essere necessario aggregare i risultati
Ci sono due funzioni principali per farlo: alle any
all any
Le funzioni alle anyhanno il comportamento seguente:
all(<vettore>) % true se tutti gli elementi sono true
any(<vettore>) % true se almeno un elemento è true
■ Se i vettori contengono numeri, ogni valore diverso da 0è true
Qualche esempio:
a = [1, 2, 3];
all(a < 3) % all([1, 1, 0]) --> false
any(a < 2) % any([1, 0, 0]) --> true
Se applicate a matrici, alled anyoperano colonna per colonna
■ Esattamente come sum
Possiamo accedere ad un vettore/matrice usando valori logici
Sintassi:
<vettore/matrice A>(<vettore/matrice I>)
■ I due vettori/matrici devono avere la stessa dimensione
Semantica:
■ Restituisce un vettore contente gli elementi di A...
■ ...Alle posizioni in cui Icontiene il valore true
A che serve? Vediamo qualche esempio...
Supponiamo di avere:
a = [1, 2, 3; 4, 5, 6; 7, 8, 9]; % [1, 2, 3]
% [4, 5, 6]
% [7, 8, 9]
Proviamo a selezionare i valori maggiori di 3:
ii = (a > 3) % denota [0, 0, 0]
% [1, 1, 1]
% [1, 1, 1]
a(ii) % denota [4, 5, 6, 7, 8, 9]
In forma compatta:
a(a > 3)
Octave: Altre Istruzioni di Iterazione
e Teorema del Programma Strutturato
Supponiamo di voler calcolare la somma della serie:
∞
ζ (s) = ∑
n=1
1
n
s
■ Si tratta della funzione Zeta di Riemann (per num. reali)
■ Converge per s > 1
Supponiamo di voler calcolare la somma della serie:
∞
ζ (s) = ∑
n=1
1
n
s
■ Si tratta della funzione Zeta di Riemann (per num. reali)
■ Converge per s > 1
Proviamo ad abbozzare un algoritmo:
z = 0;
for n = 1:???? % Il problema è qui
z = 1 / n.^s
end
■ La serie ha infiniti termini!
Supponiamo di voler calcolare la somma della serie:
∞
ζ (s) = ∑
n=1
1
n
s
■ La funzione non è computabile nel caso generale
■ Però possiamo approssimarla!
Tipicamente: ci si ferma ad un certo livello di precisione
■ Sia ζ (s) la nostra approssimazione dopo k iterazioni
■ Ci fermiamo quando ∣ζ (s) − ζ − (s)∣ < ε
■ Dove ε è la tolleranza che riteniamo accettabile
k
k
k
1
Però ancora non sappiamo a priori il numero di iterazioni...
while
Ci serve una istruzione di iterazione capace di:
■ Fermarsi in base al soddisfacimento di una condizione
■ Anziché alla fine di un vettore
Una istruzione di questo tipo è il ciclo while
Sintassi:
while <espressione> % vera o falsa
<corpo>
end % oppure endwhile (incompatibile con matlab)
■ Il corpo viene ripetuto
■ Fintanto che <espressione>denota true
Possiamo usare whileper scrivere un algoritmo per ζ (s):
∞
ζ (s) = ∑
n=1
1
n
s
tol = 1e-9;
% tolleranza
z = 0;
% val. della somma
n = 1;
old_z = -Inf;
% vecchio z
while abs(z - old_z) > tol % abs = valore assoluto
old_z = z;
% memorizzo il vecchio z
z = z + 1 ./ n.^s;
n = n + 1;
% incremento n
end
■ Infè un valore speciale che denota ∞
Il ciclo whilenon fornisce garanzie di terminazione
Per esempio:
n = 1;
s = 0;
while n < 10
s = s + n;
end
■ Questo ciclo non termina
■ Perché nnon viene incrementato!
Se vi capita, niente panico: basta premere [ctrl+c]
break continue
In alcuni casi può essere utile interrompere un ciclo
Lo si può fare con l'istruzione break
■ Vediamo un esempio sulla Zeta di Riemann
...
while true
% Di base, non smetto mai di iterare
old_z = z;
z = z + 1 ./ n.^s;
n = n + 1;
if abs(z - old_z) < tol
break
end
end
■ Qui break è usato per controllare la condizione in fondo al ciclo
Le istruzioni condizionali e di iterazione:
■ Sono anche note come istruzioni di controllo di flusso
■ Sono importanti per un particolare risultato:
Teorema del Programma Strutturato: la composizione
di istruzioni per sequenza, condizione ed iterazione è
sufficiente a codificare qualunque algoritmo
■ Composizione per sequenza = scrivere le istruzioni una dopo l'altra
Quasi tutti i linguaggi si basano su questi tre metodi di composizione
Octave: File di Script
Riguardiamo un altro il nostro codice per ζ (s):
tol = 1e-9;
z = 0;
n = 1;
old_z = -Inf;
while abs(z - old_z) > tol
old_z = z;
z = z + 1 ./ n.^s;
n = n + 1;
end
■ È lunghetto... e scomodo da inserire una riga per volta
■ Sarebbe utile poterlo scrivere a parte e poi eseguirlo
Per fortuna si può! Anzi, è il modo normale di lavorare in Octave
In Octave, si chiama file di script:
■ Un file di testo (tipo quelli di blocco note)...
■ ...Che termina con l'estensione .m...
■ ...E che contiene una sequenza di istruzioni
In sostanza, è un programma scritto in Octave
Un file di script può essere eseguito con la sintassi:
<nome del file senza ".m">[invio]
■ Per esempio zeta.msi esegue con zeta+ [invio]
■ Il file deve essere nella cartella corrente
Eseguire uno script equivale a scrivere le sue istruzioni sul prompt
Scegliete il nome con un filo di attenzione
■ Se chiamate lo script pi.m...
■ ...quando scrivete l'istruzione pi+[invio]...
■ ...eseguirete lo script, invece di ottenere il valore di π !
Soluzione: aggiungete sempre un prefisso/suffisso al nome del file
■ Io li chiamo chXX_nomescript.m
■ XX è il numero del "capitolo" del corso
È possibile inserire commenti nel codice:
Sintassi:
% Ecco un commento!
% Finalmente è chiaro cosa siano questi "cosi" :-)
■ Il testo che segue il simbolo %viene ignorato
Commentare aiuta a ragionare e ricordare
■ Un giorno un programmatore incappò in questo commento :-)
% When I wrote this, only God and I knew
%
what I was doing
% Now, God only knows
Mantenete il codice leggibile
■ Usate l'indentazione
■ Non comprimete troppe operazioni in una sola riga
sum(all(a(1:2:end, :)(:) * b')) % UNA BRUTTA IDEA
■ Se una riga è troppo lunga, potete andare a capo con "..."
a = [1, 2, 3, 4, 5, 6, 7, 8, ...
9, 10, 11, 12, 13, 14]
■ Ma soprattutto: ricordatevi di commentare
Octave: Definizione di Funzioni
Supponiamo di avere il nostro algoritmo per ζ (s) in uno script:
s = 3;
tol = 1e-9;
z = 0;
old_z = -1;
n = 1;
while abs(z - old_z) > tol
old_z = z;
z = z + 1 ./ n.^s;
n = n+1;
end
Supponiamo di avere il nostro algoritmo per ζ (s) in uno script:
s = 3; % <-- il problema è qui
tol = 1e-9;
z = 0;
old_z = -1;
n = 1;
while abs(z - old_z) > tol
old_z = z;
z = z + 1 ./ n.^s;
n = n+1;
end
■ Possiamo eseguirlo molte volte, ma sempre con s = 3
■ Per cambiare s, dobbiamo modificare lo script
■ Se ζ (s) va valutata spesso, diventa molto scomodo
Gli script in Octave sono utili per:
■ Effettuare test
■ Eseguire una analisi di dati specifici
■ Risolvere un preciso problema numerico
Non sono adatti a definire un algoritmo generico!
Per tali casi possiamo definire una nuova funzione
■ Una funzione è un sotto-programma (come uno script)
■ Ma può ricevere dei parametri
■ E può restituire un risultato
Per definire una nuova funzione si usa la sintassi:
function <ret> = <nome funz.>(<p1>, <p2>, ...)
<corpo>
end % oppure endfunction (incompatibile con Matlab)
■ <fn>è il nome della funzione
■ <pXX>sono nomi di variabili (si chiamano parametri formali)
■ <ret>è il nome della variabile da restituire
Usiamo come esempio la nostra Zeta di Riemann:
function z = zeta(s)
...
end
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ La chiamata segue il paradigma cliente-servitore
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Cliente = il chiamante (lo script, in questo caso)
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Servitore = la funzione chiamata
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Valutazione dei i parametri nel chiamante (detti parametri attuali)
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Viene allocata memoria per la funzione (il suo record di attivazione)
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ I valori dei parametri attuali sono copiati nei parametri formali
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Viene eseguila la funzione
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ La funzione definisce variabili nel suo record di attivazione
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ La funzione termina (zvale circa 1.2)
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Il valore della variabile di nome <ret>viene copiato nel chiamante
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Il record di attivazione viene distrutto
Cosa succede quando eseguiamo (e.g.) zeta(3)+[invio]?
■ Il risultato viene assegnato as ans(zeta è stata chiamata dal prompt)
Alcune osservazioni
1) Se la funzione non definisce una variabile di nome <ret>:
■ Non viene restituito nulla (niente copia verso il chiamante)
2) Le variabili definite dalla funzione:
■ Vengono distrutte insieme al record di attivazione
■ Esistono solo per la durata della chiamata
3) Il corpo di una funzione può contenere una chiamata a fuzione
■ zeta(s)per esempio chiama gli operatori aritmetici
■ Avremmo potuto chiamare qualunque funzione, però
Abbiamo visto che:
■ Lo script principale ha il suo spazio di memoria
■ Ogni chiamata a funzione ha un suo record di attivazione
Entrambi si chiamano ambienti
Le variabili definite in un ambiente sono visibili solo
quando il flusso di controllo è all'interno di esso
■ Conseguenza: le variabili definite da una funzione si dicono locali
■ Ci sono eccezioni, ma non le vedremo
È possibile ispezionare l'ambiente corrente utilizzando i comandi:
who % stampa i nomi delle variabili definite
whos % stampa i nomi + altre informazioni
È possibile eliminare una variabile utilizzando:
clear <nome variabile>
È possibile eliminare tutte le variabili con:
clear all
Ci sono diverse alternative, ma ne considereremo solo una:
Una funzione va definita all'interno di un file di funzione
Un file di funzione è un file di testo che:
■ Ha lo stesso nome della funzione, con estensione .m
■ Ha come prima istruzione la definizione della funzione
Per esempio:
% nel file "zeta.m":
function z = zeta(s)
...
end
Quando proviamo ad invocare (e.g.) zeta(3):
■ Octave cerca un file di nome "zeta.m"
■ Se lo trova, legge la definizione della funzione e la esegue
La ricerca avviene all'interno di una serie di cartelle:
■ Una di esse è sempre la cartella corrente
■ È qui che possiamo mettere i nostri file di funzione
Attenzione ai nomi!
■ Se definiiamo una funzione sumin "sum.m"
■ "Oscuriamo" la funzione sumdi Octave