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