FORTRAN:
Transcript
FORTRAN:
ELEMENTI DI PROGRAMMAZIONE a.a. 2013/14 FORTRAN: ADVANCED FEATURES Andrea Prevete, 2014 PUNTATORI E SEZIONI DI ARRAY Utilizzare i puntatori per far riferimento a sezioni di array è un potente strumento di progetto, purchè si sia ben consapevoli di alcune importanti regole/limitazioni connesse a tale scelta. Si considerino attentamente gli esempi che seguono tratti da “Fortran 90, Student Notes, University of Manchester”. REAL, DIMENSION (:), POINTER :: pv1 REAL, DIMENSION (:, :), POINTER :: pv2 REAL, DIMENSION (-3:5), TARGET :: tv1 REAL, DIMENSION (5, 10), TARGET :: tv2 INTEGER, DIMENSION(3) :: v = (/ 4, 1, -3 /) pv1 => tv1 ! pv1 è un alias per tv1 pv1 => tv1(:) ! pv1 punta a tv1 come sezione pv1 => tv2(4, :) ! pv1 punta alla 4^ riga di tv2 pv2 => tv2(2:4, 4:8) ! pv2 punta ad una sezione di tv2 pv1 => tv1(1:5:2) ! pv1 punta ad una sezione di tv1 pv1 => tv1(v) ! Non valido Andrea Prevete, 2014 PUNTATORI E SEZIONI DI ARRAY Andrea Prevete, 2014 PUNTATORI E SEZIONI DI ARRAY Andrea Prevete, 2014 PROCEDURE RICORSIVE Il fatto che una procedura (funzione o subroutine) possa richiamare se stessa è una caratteristica che aumenta considerevolmente l’appeal di un linguaggio di programmazione. Pur potendo, infatti, fare a meno delle chiamate ricorsive senza che il linguaggio stesso ne risenta in termini di capacità risolutiva – molti problemi hanno una naturale rappresentazione in termini di algoritmi ricorsivi. FORTRAN consente l’utilizzo ricorsivo di una procedura a patto di anteporre la parola chiave RECURSIVE al nome della procedura. In particolare, poi, per le funzioni ricorsive è necessario definire un’ulteriore variabile utilizzando la parola chiave RESULT per evitare possibili ambiguità. Vediamo qualche semplice esempio. Andrea Prevete, 2014 UN ESEMPIO RECURSIVE FUNCTION REVERSE(FRASE)& RESULT(FLIPPED) CHARACTER(*) FRASE CHARACTER(LEN(FRASE)) FLIPPED L = LEN_TRIM(PHRASE) N = INDEX(FRASE(1:L), " ", BACK = .TRUE.) IF (N == 0) THEN FLIPPED = FRASE ELSE FLIPPED = FRASE(N+1:L) // " " & // REVERSE(FRASE(1:N-1)) END IF END FUNCTION REVERSE Andrea Prevete, 2014 UN ALTRO CLASSICO ESEMPIO RECURSIVE FUNCTION fatt(n) RESULT (f) IMPLICIT NONE INTEGER INTENT(IN) :: n INTEGER :: f IF (n == 1) THEN f=1 ELSE f=n*fatt(n-1) ENDIF END FUNCTION fatt Andrea Prevete, 2014 ANCORA UN CLASSICO ESEMPIO NUMERICO PROGRAM ESEMPIOF INTEGER N, FINALE, FIBON READ(*,*) N FINALE = FIBON(N) WRITE(*,*) “IL VALORE DEL NUMERO DI FIBONACCI ",N," E’", FINALE END PROGRAM RECURSIVE FUNCTION FIBON(N) RESULT (FIB) INTEGER :: N, FIB IF (N <= 2) THEN FIB = 1 ELSE FIB = FIBON(N-1) + FIBON(N-2) ENDIF END FUNCTION Andrea Prevete, 2014 RICORSIONE VS CICLI INNESTATI Un’applicazione importante delle procedure ricorsive è connessa a situazioni in cui dobbiamo usare un numero grande e/o comunque non determinabile a priori di cicli do innestati: DO DO DO ……… END DO END DO END DO Immaginiamo, ad esempio, di dover gestire una tabella con un numero di dimensioni non prevedibile a tempo di progetto (quindi array di rango 1 e gestione esplicita dell’organizzazione multidimensionale!). Una classica scansione degli elementi per dimensioni successive (righe, colonne, etc) richiede quindi tanti do innestati quante sono le dimensioni. Ma se scelgo questa soluzione di progetto devo esplicitamente scrivere il codice per i cicli e .. come faccio, se non ne conosco il numero? Una soluzione naturale è proprio la chiamata ricorsiva di una funzione di scansione lungo la dimensione successiva, fino ad esaurimento delle dimensioni della tabella! Andrea Prevete, 2014 POLIMORFISMO Una interessante ed innovativa caratteristica del FORTRAN 90 (che in qualche modo anticipa alcune caratteristiche OOP del FORTRAN 2003) è sicuramente la possibilità di definire procedure generiche, nel senso di procedure capaci di accettare ed adattare il comportamento ad argomenti differenti. Per definire e quindi poter utilizzare una procedura generica è necessario farne precedere l’uso da un particolare blocco interfaccia la cui struttura è la seguente: INTERFACE nome-generico ! interfaccia specifica ! interfaccia specifica ! ………… END INTERFACE E’ bene sottolineare come la genericità delle procedure così definite non si spinge fino al punto da riunire assieme comportamenti da funzione e subroutine. Quindi la procedura generica riunirà assieme solo funzioni o solo subroutine. Vediamo qualche esempio che possa chiarire l’utilizzo di questa importante feature. Andrea Prevete, 2014 POLIMORFISMO SUBROUTINE rswap IMPLICIT NONE REAL, INTENT(INOUT) :: a,b REAL :: temp temp=a a=b b=temp END SUBROUTINE rswap SUBROUTINE iswap IMPLICIT NONE INTEGER, INTENT(INOUT) :: & a,b temp=a a=b b=temp END SUBROUTINE iswap Per poter utilizzare un unico nome (ad esempio, CALL swap(x,y) sia nel caso di x ed y interi che reali occorre definire la relativa routine generica utilizzando un blocco interfaccia: INTERFACE swap SUBROUTINE rswap (a,b) REAL, INTENT(INOUT) :: a,b END SUBROUTINE rswap SUBROUTINE iswap (a,b) INTEGER, INTENT(INOUT) :: & a,b END SUBROUTINE iswap END INTERFACE Andrea Prevete, 2014 OVERLOADING Una caratteristica ancora più innovativa del FORTRAN 90 è la possibilità di estendere il dominio degli operatori intrinseci del linguaggio. Ancora una volta la condicio sine qua non per approfittare di questa possibilità è la realizzazione di un opportuno blocco interfaccia: INTERFACE OPERATOR (operatore-intrinseco) interfaccia specifica END INTERFACE Andrea Prevete, 2014 OVERLOADING Supponiamo, ad esempio, di voler utilizzare l’operatore ‘+’ per concatenare due stringhe ignorando gli spazi vuoti. Potremmo costruire un modulo come quello di seguito riportato. Quindi, in ogni unità di programma che usa il modulo si potrà scrivere un’istruzione come c = a + b (con a, b, c variabili carattere) ottenendo l’effetto di concatenazione voluto. MODULE overloading IMPLICIT NONE ... INTERFACE OPERATOR (+) MODULE PROCEDURE concat END INTERFACE ... CONTAINS FUNCTION concat(a, b) IMPLICIT NONE CHARACTER (LEN=*), INTENT(IN) :: a, b CHARACTER (LEN=(LEN_TRIM(a) + & LEN_TRIM(b))) :: concat concat = TRIM(a)//TRIM(b) END FUNCTION concat ... END MODULE overloading Andrea Prevete, 2014 ENCAPSULATING & INHERITANCE FORTRAN 2003 introduce esplicitamente costrutti che rendono possibile un autentico approccio Object Oriented al progetto software. Ci soffermiamo in particolare sull’ incapsulamento e sull’ereditarietà. Nel frammento di codice a destra osserviamo la definizione di un tipo derivato che incapsula una procedura (metodo, nei linguaggi OO). Quindi la definizione di tipi che ereditano le proprietà del tipo base, eventualmente aggiungendone di nuove. type poligono integer :: colore_est integer :: colore_int integer :: x_pos integer :: y_pos contains procedure :: inizializza end type shape type, EXTENDS ( poligono ) :: & rettangolo integer :: lun integer :: lar end type rettangolo type, EXTENDS ( rettangolo ) :: & quadrato end type quadrato Andrea Prevete, 2014 ENCAPSULATING & INHERITANCE Utilizzando le precedenti definizioni di tipo è quindi possibile dichiarare ed inizializzare i relativi oggetti. ! dichiarazione oggetto type(poligono) :: p ! dichiarazione oggetto type(rettangolo) :: r ! dichiarazione oggetto type(square) :: q ! Inizializza p call p%initialize(1, 0, ! Inizializza r call rect%initialize(2, ! Inizializza q call q%initialize(3, 2, di tipo poligono di tipo rettangolo di tipo quadrato 10, 20) 1, 100, 200, 50, 25) 400, 500, 30, 30) Andrea Prevete, 2014 THE END Andrea Prevete, 2014