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