viewgit/index.php:465 Only variables should be passed by reference [2048]
viewgit/index.php:466 Non-static method GeSHi::get_language_name_from_extension() should not be called statically [2048]
\chapter{Il fortran 95} Il linguaggio di programmazione usato per implementare gli algoritmi è stato il FORTRAN, una revision -- ogni suggerimento è bene accetto} del FORTRAN 90. \\ Non è un linguaggio di programmazione particolarmente recente ma si adatta piuttosto bene ad implementare gli algoritmi studiati nel corso di Calcolo Scientifico; questi sarebbe stati infatti molto meno performanti se realizzati con Matlab, Octave o programmi simili. In ogni caso, in queste note includerò alcune righe su come si scrivono le prime righe di codice in fortran. In codice Fortran saranno infatti riportati tutti gli esempi nella parte di esercitazione. Per una convenzione che di cui non so l'origine non è posibbile cominciare da qualcosa di diverso del programma Hello World! Noi non saremo da meno. Cominceremo a guardare il codice e a commentarlo passo passo.. \begin{lstlisting}[frame=tb,caption=Hello World,label=lst:helloworld] PROGRAM helloworld IMPLICIT NONE WRITE(*,*) 'Hello World' END PROGRAM \end{lstlisting} Tutte le righe precedute da \lstinline-!- sono dei commenti e quindi vengono scartate dal compilatore. Ad esempio '\lstinline-a = 1 ! Inizializzazione-' è equivalente a '\lstinline-a = 1-'. L'istruzione \lstinline-PROGRAM helloworld- spiega al compilatore che questo è l'inizio del nostro programma e che si chiamerà helloworld. Si aspetta di trovare alla fine un \lstinline-END PROGRAM- che noi abbiamo prontamente inserito. L'istruzione \lstinline-IMPLICIT NONE- serve invece a disattivare delle convenzione che si usavano nelle prime versione del fortran (e che esistono ancora per retrocompatibilità) che stabiliscono che le variabili \lstinline-i,j,k- sono di tipo intero, mentre ad esempio \lstinline-x,y,z- sono reali. Questo non è desiderabile in un linguaggio di programmazione moderno in cui ci si aspetta di scrivere del codice comprensibile. dei casi, coincide con il monitor} tutti gli argomenti che gli si passano dopo. \`E possibile controllare la formattazione di quello che viene stampato ma, come è simboleggiato dagli asterischi, per ora non ce ne stiamo preoccupando. Un programma in FORTRAN ha una struttura prefissata: \begin{lstlisting}[label=lst:fotranstruct,frame=tb] PROGRAM nomeprogramma IMPLICIT NONE ! Specificazioni: in questa parte si trova la dichiarazione ! delle variabili e dei nuovi tipi ! Parte di esecuzione: qui troviamo il codice vero e proprio che ! viene eseguito ! Subroutine: qui troviamo la definizione dei sottoprogrammi che ! possono venire chiamati dalla parte di esecuzione END PROGRAM \end{lstlisting} Come il C e tutti i linguaggi che vantano una certa storia il FORTRAN è un linguaggio tipato, ovvero ogni variabile ha un tipo (ad esempio intero) e non gli si possono assegnare valori di tipi differenti. \\ Si possono aggiungere delle parole chiave alle dichiarazioni di tipi, più precisamente \begin{itemize} un parametro per il programma. allocata esplicitamente in un secondo momento, è particolarmente utile con l'altra parola chiave e la sua relativa dimensione (Esempio: \lstinline- INTEGER, DIMENSION(5) :: vec-); è possibile specificare \lstinline-:- come dimensione, nel qual caso il compilatore assumerà che la dimensione verrà determinata in un secondo momento. Un caso tipico è la creazione di un vettore la cui dimensione verrà determinata a run-time\footnote{ovvero durante l'esecuzione.}; questo si può realizzare con un codice di questo tipo \begin{lstlisting}[frame=tb] PROGRAM allocatore INTEGER , ALLOCATABLE :: i INTEGER :: size ! Leggo da tastiera la dimensione dell'array e lo alloco READ(*,*) size ALLOCATE(i(size)) END PROGRAM \end{lstlisting} \end{itemize} I tipi principali presenti in Fortran sono \begin{itemize} interi. Generalemente occupano $4$ byte. è \lstinline-REAL- e occupano generalmente $4$ byte. Per i nostri scopi questi sono pochi, e quindi useremo in generale il seguente trucchetto: \begin{lstlisting}[frame=tb] INTEGER, PARAMETER :: db = KIND(0.D0) REAL(dp) :: real_var \end{lstlisting} che si occuperà di assegnare al tipo \lstinline-REAL- dichiarato la stessa dimensione delle costante di tipo \end{itemize} Per iniziare ci basterà conoscere le istruzioni di stampa su schermo e lettura da tastiera, che sono rispettivamente \begin{itemize} \item \lstinline-WRITE(*,*) dato_1, dato_2 , ...- che si occuperà di scrivere sullo schermo (identificato dal primo \lstinline-*-) e con una formattazione standard (identificata dal secondo \lstinline-*-) i dati che gli inseriamo dopo; \item \lstinline-READ(*,*) var_1, var_2, ...- che si occuperà di leggere da tastiera (il primo \lstinline-*-) i dati in un formato standard (il secondo \lstinline-*-); \end{itemize} In realtà queste istruzioni ci permettono di controllare anche formattazioni e sorgenti e destinazioni di Input e Output. Per scrivere (o leggere) su un file è prima necessario \textbf{aprirlo}. Per fare questo bisogna eseguire una chiamata alla funzione \lstinline-OPEN()-. Questa si occuperà di aprire un file da noi indicata ed assegnarlo ad un numero intero, anche quello indicato da noi. Bisogna fare attenzione in quest'ultimo passaggio a ricordare che \lstinline-5- e \lstinline-6- sono già riservati alla tastiera ed allo schermo. Potremmo poi fare riferimento (sia in lettura che in scrittura) a quel determinato file sostituendo quell'intero a \lstinline-*-. Questo codice probabilmente chiarirà meglio la questione. \begin{lstlisting}[frame=tb] PROGRAM CopiaIncolla IMPLICIT NONE ! Dichiaro delle variabili intere di tipo PARAMETER ! per contenere gli interi con cui poi mi riferiro' ai file. ! Sarebbe infatti complicato dover ricordare tanti numeri ! a memoria e renderebbe il codice meno leggibile INTEGER, PARAMETER :: infile = 11, outfile = 12 INTEGER :: j INTEGER :: buf ! Cerco di aprire il file in.txt OPEN(FILE='in.txt', UNIT=infile) ! E anche il file out.txt OPEN(FILE='out.txt', UNIT=outfile) ! Leggo le prime 10 righe da questo file ! e le scrivo su out.txt, raddoppiate (assumendo ! che siano interi) DO j=1,10 READ(infile,*) buf WRITE(outfile,*) buf * 2 END DO ! Alla chiusura del programma anche tutti i file vengono chiusi, ! e quindi questo rende le prossime righe superflue. Pero' e' buona ! norma ricordarsi di chiudere i file che si sono aperti, e quindi ! lo facciamo CLOSE(infile) CLOSE(outfile) END PROGRAM \end{lstlisting} Abbiamo qui introdotto anche la chiamata \lstinline-CLOSE()- che serve appunto per chiudere un file. \`E normalmente buona norma chiudere tutti i file che si aprono e quindi, in programma corposi, conviene sempre scrivere la chiamata ad \lstinline-OPEN()- insieme alla chiamata \lstinline-CLOSE()- per evitare di dimenticarla. Mano a mano che il programma cresce diventerà necessario razionalizzare la disposizione di funzioni e strutturare meglio il listato. In particolare siamo interessati a scrivere delle funzioni che si occupino di svolgere un determinato compito in modo che poi possiamo chiamarle senza preoccuparci (troppo) di come sono implementate. per una subroutine assomiglia molto a quella di un programma: \begin{lstlisting}[frame=tb,caption=Una subroutine] SUBROUTINE nome(...) ! [ Dichiarazioni ] ! [ Corpo della subroutine ] END SUBROUTINE \end{lstlisting} Una particolarità per così dire ``curiosa'' è che le variabili passate alla subroutine dovranno essere ridichiarate all'interno della stessa perché essa ci possa accedere. Le variabili inoltre vengono sempre passate per riferimento (quello che succede in C passando per riferimento o quello che succede in C++ passando una reference) e, per conludere, non c'è un'istruzione di ritorno\footnote{% O perlomeno, non ne ho mai incontrate. Notare però che programmo in fortran da pochi giorni e quindi.. potrei scoprirle prossimamente :) }. anche delle funzioni, che come in C ritornano dei valori; noi non le scriveremo (e quindi in particolare non ne ho mai scritta una) e quindi non sono qui esposte. Nonostante questo verranno utilizzate nel corso funzioni interne del fortran come \lstinline-RESHAPE()- o \lstinline-MATMUL()-. }, ma tutte insieme nella chiamata della funzione. Per chiarire le idee, e spiegare i passaggi che mancano, proponiamo un esempio di una funzione che prenda un numero e lo moltiplichi per due\footnote{che esempio inutile, direte voi\ldots e avete anche ragione ma in questo momento non mi viene in mente niente di più costruttivo} \begin{lstlisting}[frame=tb,caption=Un semplice programma che fa uso di subroutine] ! Questo programma prende in input un numero e lo moltiplica per due; ! Di per se' e' piuttosto inutile ma mostra come si utilizza una subroutine ! nell'ambito di un programma PROGRAM sub_doppio REAL :: x; ! Leggo dallo standard input x READ(*,*) x; ! Lo moltiplico per due facendo uso della subroutine CALL doppio(x); ! Lo stampo. Atttenzione! La subroutine ha modificato il vero ! valore di x perche' le ho di fatto passato un puntatore WRITE(*,*) x; ! Terminiamo l'esecuzione del programma principale. END PROGRAM sub_doppio SUBROUTINE doppio(x) REAL :: x; x = x * 2; END SUBROUTINE doppio \end{lstlisting} Naturalmente si possono fare esempi molto più complessi, e soprattutto più utili. Ad esempio, supponiamo di implementare la subroutine \lstinline-dotprod- che avevamo pensato parlando del pagerank per fare il prodotto matrice vettore con una matrice sparsa. Non la riportiamo qui perché è già stata scritta (si veda il Listato~\ref{lst:matrixvec}) ma commentiamo alcuni fatti: \begin{itemize} \item Le keyword \lstinline-INTENT(IN)- e \lstinline-INTENT(OUT)- servono per l'appunto a specificare se una variabile è da considerarsi di input o di output. Questo di fatto non cambia nulla, a parte che il compilatore ci avviserà se tentiamo di modificare variabili di input; \item La \lstinline-SUBROUTINE- può venir scritta in un file separato e poi linkata insieme al file eseguibile che la richiama. Spiegheremo meglio questo concetto con un esempio; \end{itemize} Supponiamo di avere scritto la subroutine in un file di nome \verb-dotprod.f90-, e di aver successivamente scritto un listato \verb-matrici.f90- in cui compaia l'istruzione \lstinline-CALL dotprod(...)-. Sarebbe tedioso dover copiare il contenuto del file \verb-dotprod.f90- all'interno di \verb-matrici.f90- per poi compilarlo. In effetti esiste un metodo più pratico per agire. \begin{enumerate} che contengono la versione compilata del listato, senza che le referenze siano stare risolto. Questo significa che all'interno del file oggetto associato a \verb-matrici.f90- ci sarà una chiamata ad una \lstinline-dotprod()- senza che abbia la minima idea di dove andare a prenderla; questa operazione si realizza su Linux con \verb-f95- in questo modo: \\ \verb+# f95 -c dotprod.f90 matrici.f90+ \item Il secondo passo consiste nel creare un eseguibile unico con le dipendenze risolte, ovvero in cui ci sia la chiamata a \lstinline-dotprod()- ma questa volta sia chiaro dove poterla trovare\footnote{sono conscio che questa spiegazione non è molto chiara. Cercherò di migliorarla}; questo compito viene eseguito dal \emph{linker} che, su Linux, fa ancora parte di \verb-f95-\footnote{forse si portebbe discutere sulla correttezza di questa affermazione, in ogni caso noi daremo il comando f95 per eseguire l'operazione. Quello che poi lui faccia davvero, compreso chiamare altri programmi, per il momento non è affar nostro}; il comando è il seguente: \\ \verb+# f95 -o matrici matrici.o dotprod.o+ \end{enumerate}