FORTRAN

noukist
Νεοφερμένος
Posts: 20
Joined: 2007-12-10
Coins: 6
Dobro member
User is offline
FORTRAN

Η γλώσσα FORTRAN (από τα αρχικά FORmulae TRANslator - μεταφραστής τύπων) είναι μία από τις πρώτες γλώσσες υψηλού επιπέδου, η οποία χρησιμοποιήθηκε κυρίως σε επιστημονικές αλλά και σε εμπορικές εφαρμογές. Δημιουργήθηκε τη δεκαετία του 1950 από την ΙΒΜ και χρησιμοποιείται μέχρι και σήμερα. Αρχικά η FORTRAN ήταν προσανατολισμένη στην επίλυση μαθηματικών προβλημάτων.

ΕΚΔΟΣΕΙΣ

Υπάρχουν οι εξής 5 τυποποιημένες εκδόσεις της FORTRAN: -FORTRAN-66 -FORTRAN-77 -FORTRAN-90 -FORTRAN-95 -FORTRAN 2003

ΑΛΦΑΒΗΤΟ

Ένα πρόγραμμα γραμμένο σε έκδοση της FORTRAN μέχρι και την FORTRAN-77 μπορεί να χρησιμοποιήσει οποιουδήποτε από τους εξής χαρακτήρες: -Τα 26 κεφαλαία γράμματα του αγγλικού αλφαβήτου: A, B, ..., Z -Τους 10 αραβικούς αριθμούς: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -Τους 12 ειδικούς χαρακτήρες: + - * / = ( ) ' . , $ : -Το κενό διάστημα.

Από την έκδοση FORTRAN-90 και μετά, προστέθηκαν επιπλέον ειδικοί χαρακτήρες: -! " & ; < > ? -Τα 26 μικρά γράμματα του αγγλικού αλφαβήτου: a, b, ..., z

ΤΥΠΟΙ ΕΝΤΟΛΩΝ

Οι εντολές της FORTRAN-77 είναι δηλωτικές ή εκτελέσιμες:

Δηλωτικές εντολές:

PROGRAM, FUNCTION, SUBROUTINE, BLOCKDATA IMPLICIT PARAMETER INTEGER, REAL, DOUBLEPRECISION, COMPLEX, LOGICAL, CHARACTER DIMENSION COMMON, EQUIVALENCE EXTERNAL, INTRINSIC, ENTRY Ορισμός συνάρτησης – πρότασης SAVE DATA FORMAT

Δηλωτική εντολή τέλους κειμένου προγράμματος

END

Εκτελέσιμες εντολές:

OPEN, CLOSE, INQUIRE, READ, WRITE, PRINT, PUNCH REWIND, BACKSPACE, ENDFILE (προσάρτηση τιμής) = ASSIGN .. TO προσαρτημένο GOTO, χωρίς συνθήκη GOTO, υπολογιζόμενο GOTO αριθμητικό IF, λογικό IF, IF .. THEN .. ELSEIF .. ELSE .. ENDIF DO .. CONTINUE CALL, RETURN PAUSE, STOP

Σε επόμενη έκδοση της γλώσσας προστέθηκαν :END DO, WHILE.

ΔΟΜΗ ΠΡΟΓΡΑΜΜΑΤΩΝ

'''Κατά στήλες'''

-Η πρώτη ομάδα, που περιέχει τις στήλες 1-6, χρησιμοποιείται για την εισαγωγή των συμβόλων σχολίων (* και c), των αριθμών εντολών και χαρακτήρων συνέχισης γραμμής. Ειδικότερα:
1.Τα σύμβολα * ή c είναι προαιρετικά και εισάγονται στην πρώτη στήλη.
Ο μεταγλωττιστής αναγωρίζει έτσι την αντίστοιχη γραμμή ως σχόλιο, και την προσπερνάει.
Τα σχόλια δεν έχουν επίδραση στον εκτελέσιμο κώδικα, αλλά βοηθάνε τον προγραμματιστή στην κατανόηση
και εκσφαλμάτωση του προγράμματος.
2.Οι αριθμοί εντολών είναι προαιρετικοί 5-ψήφιοι αριθμοί, που εισάγονται στις στήλες 1 ως 5.
Είναι χρήσιμοι για εντολές ανακατεύθυνσης της ροής του προγράμματος,
αν και σπάνια πια χρησιμοποιούνται μετά την εισαγωγή της έννοιας του δομημένου προγραμματισμού.
3.Ένας οποιοσδήποτε χαρακτήρας, εκτός του κενού διαστήματος και του μηδέν, υποδεικνύει στον μεταγλωττιστή ότι η αντίστοιχη γραμμή είναι συνέχεια της προηγούμενης. Καθίσταται έτσι δυνατή η συνέχιση μιας πολύ μεγάλης εντολής σε περισσότερες της μίας γραμμής. Ο μέγιστος αριθμός γραμμών για μια εντολή είναι 40.
-Στην δεύτερη, κύρια ομάδα στηλών 7-72, εισάγονται οι εντολές της FORTRAN.
-Τέλος η τρίτη ομάδα, στήλες 73-80, περιέχει κείμενο που αγνοείται από τον μεταγγλωτιστή, εκτός αν πρόκειται για δεδομένα του προγράμματος.
''Σημείωση:'' Στην FORTRAN-90, δεν υπάρχουν οι περιορισμοί των ομάδων στηλών 7-72 και 73-80. Κάθε γραμμή έχει 132 χαρακτήρες. Η συνέχιση γραμμής γίνεται με τον χαρακτήρα &. Επίσης τα σχόλια γίνονται με την τοποθέτηση του συμβόλου ! σε οποιαδήποτε στήλη, ακολουθούμενο από το σχόλιο.

'''Κατά γραμμές'''

Ένα πρόγραμμα FORTRAN μπορεί επίσης να χωριστεί κατά 4 τμήματα ως εξής:

''Επικεφαλίδα (heading)''
H επικεφαλίδα σε ένα πρόγραμμα είναι προαιρετική και εισάγεται στην πρώτη γραμμή. Προσδιορίζει την αρχή του προγράμματος και το όνομά του. Έχει την εξής συγκεκριμένη μορφή:
PROGRAM [όνομα προγράμματος]
Οι αγκύλες δεν εισάγονται. Προσδιορίζουν μια παράμετρο, όπως και στις υπόλοιπες εντολές παρακάτω.

''Τεκμηρίωση (documentation)''
Ακολουθεί η τεκμηρίωση. Είναι επίσης προαιρετικό τμήμα, το οποίο περιέχει σχόλια σχετικά με το πρόγραμμα, όπως:
-Σκοπό του προγράμματος,
-Σημασία των διαφόρων μεταβλητών,
-Πληροφορίες για την είσοδο/έξοδο του προγράμματος,
-Περιγραφή τυχόν τυποποιημένων αλγορίθμων,
-Όνομα/ονόματα προγραμματιστή/προγραμματιστών,
-Ημερομηνία σύνταξης, τροποποίησης, κ.τ.λ.

Σχόλια τεκμηρίωσης μπορεί να βρίσκονται και ανάμεσα από τις εκτελέσιμες εντολές.

''Τμήμα προδιαγραφών/Τμήμα δηλώσεων (specification part)''
Στο τμήμα προδιαγραφών/δηλώσεων δηλώνονται τα ονόματα και οι τύποι των μεταβλητών ή των σταθερών που θα χρησιμοποιηθούν, οι πίνακες, κ.τ.λ.. Οι δηλωτικές εντολές γράφονται όλες πριν από τις εκτελέσιμες εντολές. (Εξαίρεση είναι η εντολή ENTRY).

''Εκτελέσιμο τμήμα (execution part)''
Τέλος, το σημαντικότερο τμήμα είναι το εκτελέσιμο. Περιέχει τις εκτελέσιμες εντολές, με τις οποίες τα δεδομένα της εισόδου μετατρέπονται σε αποτελέσματα.

''Τέλος του προγράμματος''
Το τέλος του προγράμματος δηλώνεται με την εντολή END.

ΠΕΡΙΣΣΟΤΕΡΑ ΓΙΑ ΤΟΝ ΤΡΟΠΟ ΣΥΝΤΑΞΗΣ ΤΗΣ ΓΛΩΣΣΑΣ

==== 1. Εισαγωγή ====

Ένας ηλεκτρονικός υπολογιστής έχει τη δυνατότητα να προγραμματιστεί ώστε να εκτελέσει μια συγκεκριμένη διαδικασία. Προγραμματισμός είναι η λεπτομερής περιγραφή, σε κάποια γλώσσα προγραμματισμού, των βημάτων που πρέπει να ακολουθήσει ώστε να ολοκληρώσει την επιθυμητή διεργασία. Το σύνολο των βημάτων, το πρόγραμμα δηλαδή, συνήθως απαιτεί δεδομένα που πρόκειται να επεξεργαστεί ώστε να παράγει κάποιο αποτέλεσμα· σχεδιάζεται, όμως, ανεξάρτητα από συγκεκριμένες τιμές των δεδομένων αυτών.

Οι πιο διαδεδομένες γλώσσες προγραμματισμού στις εφαρμοσμένες επιστήμες (Fortran, C, C++) χρησιμοποιούν σταθερές και μεταβλητές ποσότητες, δηλαδή, θέσεις στη μνήμη του υπολογιστή, για την αποθήκευση των ποσοτήτων (δεδομένων και αποτελεσμάτων) του προγράμματος. Ο υπολογιστής επιδρά στις τιμές αυτών των ποσοτήτων ακολουθώντας διαδοχικά τις εντολές που περιλαμβάνονται στο πρόγραμμα. Υπάρχει η δυνατότητα ανάθεσης τιμής στις μεταβλητές, επιλογής της εντολής που θα εκτελεστεί στο επόμενο βήμα, ανάλογα με κάποια συνθήκη, καθώς και η δυνατότητα επανάληψης μιας ή περισσότερων εντολών. Η βασική δομή και ο τρόπος λειτουργίας ενός προγράμματος στις προαναφερθείσες γλώσσες δε διαφέρει ουσιαστικά από τη μία στην άλλη. Αυτό δεν σημαίνει ότι κάποιες γλώσσες προγραματισμού δεν είναι πιο εξελιγμένες από άλλες--παρέχουν, δηλαδή, περισσότερες δυνατότητες--ή είναι πιο κατάλληλες για συγκεκριμένες εφαρμογές.

===== 1.1 Παράδειγμα =====

Ας εξετάσουμε μία απλή εργασία που θέλουμε να εκτελεστεί από ένα ηλεκτρονικό υπολογιστή: να μας ζητά έναν ακέραιο αριθμό και να τυπώνει το διπλάσιό του. Η διαδικασία που ακολουθούμε, ο αλγόριθμος, είναι ο εξής:

Ανάγνωση αριθμού από το πληκτρολόγιο -- [εισαγωγή του στη μνήμη].
[ανάκληση του αριθμού από τη μνήμη] -- υπολογισμός του διπλάσιου - [εισαγωγή του αποτελέσματος στη μνήμη].
[ανάκληση του αποτελέσματος από τη μνήμη] -- Εκτύπωση στην οθόνη.
Η ενέργειες που περιλαμβάνονται σε αγκύλες μπορεί να μη φαίνονται αναγκαίες σε πρώτη ανάγνωση. Όμως, είναι, καθώς ο υπολογιστής κρατά στη μνήμη RAM οποιαδήποτε πληροφορία, σε μεταβλητές ή σταθερές ποσότητες.

Πολλές γλώσσες προγραμματισμού, ανάμεσά τους και η Fortran, χρειάζονται ένα επιλέον, προκαταρτικό, βήμα αυτής της διαδικασίας. Αυτό είναι το:

Δήλωση μεταβλητών (δηλαδή, δέσμευση μνήμης).

Ας δούμε καταρχήν ένα πλήρες πρόγραμμα Fortran 95 που εκτελεί την παραπάνω εργασία ακολουθώντας τα βήματα που περιγράψαμε. Με αυτό έχουμε την ευκαιρία να δούμε βασικά στοιχεία της δομής του κώδικα:

PROGRAM twice

IMPLICIT NONE

! step 0
INTEGER :: a, b

! step 1
READ *, a

! step 2
b = 2 * a

! step 3
PRINT *, "To diplasio einai"
PRINT *, b

END PROGRAM twice

Ας το αναλύσουμε:

''1.1.1 Δομή Προγράμματος''

Οι εντολές του κυρίως προγράμματός μας (καλό είναι να) περιλαμβάνονται μεταξύ των PROGRAM ... END PROGRAM :

PROGRAM twice

IMPLICIT NONE
...
...
...
END PROGRAM twice

Με την πρώτη εντολή το πρόγραμμά μας έχει ονομαστεί “twice” . Καλό είναι να αποδίδουμε ονόματα στα προγράμματά μας που υποδηλώνουν με κάποιον τρόπο τι κάνουν αυτά.

Όλες οι εντολές/γραμμές μπορούν να ξεκινούν από οποιαδήποτε στήλη.

Οι πρώτες γραμμές μετά το PROGRAM ... συγκεντρώνουν τις δηλώσεις όλων των ποσοτήτων. Η πρώτη γραμμή από αυτές (καλό είναι να) είναι η IMPLICIT NONE . Αφού τελειώσουν οι δηλώσεις, ακολουθούν οι υπόλοιπες εντολές, με τη σειρά που θέλουμε να εκτελεστούν.

Η τελική εντολή είναι το END PROGRAM ... .

Σε ένα αρχείο με κώδικα σε Fortran 95 επιτρέπονται κενά μεταξύ λέξεων και κενές γραμμές μεταξύ εντολών.

''1.1.2 Σχόλια''
Ο compiler αγνοεί τους χαρακτήρες που υπάρχουν μεταξύ του χαρακτήρα `!' και μέχρι το τέλος της γραμμής που εμφανίζεται αυτός. Επομένως, γραμμές ή τμήματα γραμμών που αρχίζουν με `!' μπορούν να περιλάβουν επεξηγηματικά μηνύματα από τον προγραμματιστή, για τη δική του διευκόλυνση.

''1.1.3 Δήλωση Μεταβλητών''

Οι μεταβλητές (variables) είναι θέσεις στη μνήμη που χρησιμοποιούνται για την αποθήκευση των ποσοτήτων (δεδομένων, αποτελεσμάτων) του προγράμματος. Προτού χρησιμοποιηθούν πρέπει να δηλωθούν, δηλαδή να ενημερωθεί ο compiler για το όνομά τους και τον τύπο τους, αλλά και να πάρουν τιμή.

Η εντολή

INTEGER :: a, b

αποτελεί τη δήλωση των μεταβλητών του προγράμματός μας. Παρατηρήστε ότι στον αλγόριθμό μας έχουμε δύο εισαγωγές ακεραίων στη μνήμη, οπότε θέλουμε δύο κατάλληλες μεταβλητές. Η συγκεκριμένη εντολή ζητά από τον compiler να δεσμεύσει χώρο στη μνήμη για δύο ακέραιους αριθμούς ( INTEGER ) με ονόματα a και b .

''1.1.4 Ανάγνωση δεδομένων''

Η εντολή

READ *, a

“διαβάζει” από το πληκτρολόγιο έναν ακέραιο αριθμό και τον τοποθετεί στη μεταβλητή a . Το σύμβολο '*' υποδεικνύει στον μεταγλωττιστή ότι το πλήθος, τον τύπο και τη μορφή των δεδομένων που θα δεχθεί πρέπει να τα προσδιορίσει αυτόματα από τις μεταβλητές που παρατίθενται στην εντολή READ . Θα δούμε αργότερα πώς μπορούμε να του προσδιορίσουμε εμείς τις συγκεκριμένες πληροφορίες.

Η συγκεκριμένη εντολή αποτελεί τον ένα από τους δύο τρόπους για απόδοση τιμής σε μεταβλητή. Η άλλη είναι η εντολή ανάθεσης (1.5).

''1.1.5 Εντολή ανάθεσης''
Η εντολή

b = 2*a

εκτελείται ως εξής: Κσταρχήν, υπολογίζεται το δεξί μέλος. Ανακαλείται από τη μνήμη ο αριθμός που είναι αποθηκευμένος στη μεταβλητή a , εκετελείται ο πολλαπλασιασμός με το 2 . Ο πολλαπλασιασμός υποδηλώνεται με το '*' . Κατόπιν, το αποτέλεσμα της πράξης αποθηκεύεται στη μεταβλητή του αριστερού μέλους.

''1.1.6 Εκτύπωση δεδομένων''

Οι εντολές

PRINT *, "To diplasio einai"
PRINT *, b

προκαλούν διαδοχική εκτύπωση στην οθόνη δύο πληροφοριών: συγκεκριμένου κειμένου (σειρά χαρακτήρων εντός εισαγωγικών) και της τιμής που αποθηκεύεται στην μεταβλητή b (και η οποία ανακαλείται από τη μνήμη).

Παρακάτω, ακολουθούν πιο αναλυτικές περιγραφές όσων αναφέραμε στο παράδειγμα.

===== 1.2 Μεταβλητές =====

Οι ενσωματωμένοι τύποι των μεταβλητών στη Fortran είναι οι INTEGER , REAL και DOUBLE PRECISION , COMPLEX , LOGICAL , CHARACTER . Αντιστοιχούν με τη σειρά, σε ποσότητες ακέραιες, πραγματικές απλής ακρίβειας, πραγματικές διπλής ακρίβειας, μιγαδικές, λογικές και ποσότητες χαρακτήρα.

''1.2.1 Δήλωση''

Η δήλωση μιας ακέραιας μεταβλητής με όνομα π.χ. abc γίνεται με την ακόλουθη εντολή:

INTEGER :: abc

Η δήλωση δύο πραγματικών μεταβλητών απλής ακρίβειας με ονόματα π.χ. value1, value2, γίνεται με τις ακόλουθες εντολές:

REAL :: value1
REAL :: value2

ή, ισοδύναμα, με την

REAL :: value1, value2

Οι πραγματικές μεταβλητές απλής ακρίβειας έχουν συνήθως ακρίβεια 6 ψηφίων.

Σε επιστημονικούς κώδικες, η ακρίβεια των μεταβλητών τύπου REAL συχνά δεν είναι ικανοποιητική. Γι' αυτό υπάρχει ενσωματωμένος ένας πραγματικός τύπος με μεγαλύτερη ακρίβεια. Ο τρόπος που ορίζεται στη Fortran 90 είναι σχετικά πολύπλοκος, διευκολύνει, όμως, αρκετά την ανάπτυξη μεγάλων κωδίκων. Εδώ θα παρουσιάσουμε τον τρόπο που “κληρονομήθηκε” από την Fortran 77.

Η δήλωση πραγματικής μεταβλητής διπλής ακρίβειας με ονόμα π.χ. ab, γίνεται με την ακόλουθη εντολή:

DOUBLE PRECISION :: ab

Οι πραγματικές μεταβλητές διπλής ακρίβειας έχουν συνήθως ακρίβεια 15 ψηφίων.

Δήλωση μιγαδικής μεταβλητής (απλής ακρίβειας) γίνεται ως εξής:

COMPLEX :: z

Περισσότερα για μιγαδικούς θα αναπτύξουμε σε επόμενο κεφάλαιο.

''1.2.2 Όνομα''

Τα ονόματα των μεταβλητών, συναρτήσεων, προγράμματος, κλπ. είναι της επιλογής του προγραμματιστή. Σχηματίζονται με τους λατινικούς χαρακτήρες a-z τα αριθμητικά ψηφία 0 - 9 και το χαρακτήρα `_'. Το μέγιστο μήκος κάθε ονόματος είναι 31 χαρακτήρες και πρέπει να μην αρχίζει με αριθμητικό ψηφίο. Κεφαλαία και πεζά γράμματα είναι ίδια. Καλό είναι να μην αρχίζει ούτε από `_' καθώς ονόματα με αυτόν για πρώτο χαρακτήρα χρησιμοποιούνται εσωτερικά από τον μεταγλωττιστή.

''1.2.3 Απόδοση Τιμής''

Μια μεταβλητή παίρνει τιμή
“διαβάζοντάς” τη από το πληκτρολόγιο, από αρχείο κλπ. με την εντολή READ . Π.χ. για είσοδο από το πληκτρολόγιο ακέραιου αριθμού και αποθήκευση στη μεταβλητή a έχουμε

INTEGER :: a
.....
READ *, a

ή, ισοδύναμα, αλλά και πιο γενικά,

INTEGER :: a
.....
READ (*,*) a

με εντολή ανάθεσης, της μορφής
μεταβλητή = [ έκφραση με σταθερές και μεταβλητές ]
Π.χ.

REAL :: a, b
......
b = 3.0 + 2 * a

===== 1.3 Αριθμητικές Σταθερές =====

''1.3.1 Ακέραιες''

Μία σειρά αριθμητικών ψηφίων χωρίς κενά ή άλλα σύμβολα, αποτελεί μία ακέραια σταθερά. Ο πρώτος χαρακτήρας της μπορεί να είναι το πρόσημο `+ ' ή `- '. Π.χ., τρεις ακέραιες σταθερές είναι οι παρακάτω
3, -12, 123456

''1.3.2 Πραγματικές''

Μία σειρά αριθμητικών ψηφίων χωρίς κενά, που περιλαμβάνει τελεία (στη θέση της υποδιαστολής) συμβολίζει πραγματική σταθερά απλής ακρίβειας (συνήθως 6 δεκαδικών ψηφίων). Πριν ή μετά την υποδιαστολή μπορεί να μην υπάρχουν ψηφία. Ο χαρακτήρας E, αν υπάρχει, ακολουθείται από τον ακέραιο εκθέτη του 10 με τη δύναμη του οποίου πολλαπλασιάζεται ο αμέσως προηγούμενος του E αριθμός:

2.034, 0.23, .44, 23., 2e -4 (≡0.0002), 2.3E2 (≡230.0) .

Μία σειρά αριθμητικών ψηφίων χωρίς κενά, που περιλαμβάνει τελεία (στη θέση της υποδιαστολής) και ακολουθείται από το χαρακτήρα D και τον ακέραιο εκθέτη του 10 με τη δύναμη του οποίου πολλαπλασιάζεται συμβολίζει πραγματική σταθερά διπλής ακρίβειας (συνήθως 15 δεκαδικών ψηφίων). Π.χ.,

2d - 4 (≡0.0002d0), 2.3D2 (≡230.0d0) .

Προσέξτε ότι ο ίδιος πραγματικός αριθμός μπορεί να γραφεί στη Fortran ως πραγματικός είτε απλής είτε διπλής ακρίβειας1.1. Οι δύο μορφές είναι διαφορετικές. Στην περίπτωση που εμφανίζονται στην ίδια έκφραση, π.χ. σε μια πρόσθεση, ο μεταγλωττιστής μετατρέπει την σταθερά απλής ακρίβειας σε διπλής προτού προχωρήσει.

Ο συμβολισμός ενός πραγματικού αριθμού στη μορφή που περιλαμβάνει το E ή το D είναι ο λεγόμενος “επιστημονικός” . Παρατηρήστε ότι μόνο με την επιστημονική μορφή μπορούμε να προσδιορίσουμε ότι μια πραγματική σταθερά είναι διπλής ακρίβειας. Δηλαδή, το 3.2 της αριθμητικής, πρέπει να γραφεί στη Fortran ως 3.2D0 για να αποθηκευθεί ως διπλής ακρίβειας. Επίσης, δεν έχει σημασία πόσα δεκαδικά ψηφία παραθέτουμε σε μία πραγματική σταθερά. Αν είναι απλής ακρίβειας θα αγνοηθούν τα ψηφία πέρα απο το έκτο ή έβδομο σημαντικό. Αντίστοιχα, σε διπλή ακρίβεια τα πρώτα 15 ή 16 σημαντικά ψηφία κρατώνται, και θεωρούνται 0 όσα δεκαδικά μέχρι το 15ο ή 16ο δεν προσδιορίζονται. Έτσι, η μαθηματική σταθερά π = 3.14159265358979323846... μπορεί να γραφεί στη Fortran ως 3.141593 (τα επιπλέον δεκαδικά ψηφία αγνοούνται, ακόμα και αν τα γράψουμε) ή ως 3.14159265358979D 0 .

''1.3.3 Μιγαδικές''

Η μιγαδική σταθερά συμβολίζεται στη Fortran με ζεύγος πραγματικών αριθμών εντός παρενθέσεων, με (,) μεταξύ τους. Ο πρώτος αριθμός είναι το πραγματικό μέρος ενώ ο δεύτερος είναι το φανταστικό. Π.χ., ο μιγαδικός αριθμός 3.1 + 2i των μαθηματικών γράφεται ως

(3.1, 2.0) ,

ενώ ο i γράφεται ως

(0.0, 1.0) .

===== 1.4 Σταθερές Ποσότητες =====

Μια ποσότητα (οποιουδήποτε τύπου) που επιθυμούμε να πάρει τιμή και να μην μπορεί να αλλάξει κατά τη διάρκεια εκτέλεσης του προγράμματος, δηλώνεται με τη χρήση της λέξης PARAMETER . Είναι απαραίτητο να της δώσουμε αρχική (και μόνιμη) τιμή κατά τον ορισμό της (δεν μπορεί, επομένως, να διαβαστεί από το πληκτρολόγιο, αρχείο, κλπ., ή να πάρει τιμή με εντολή ανάθεσης). Π.χ. πραγματική ποσότητα με σταθερή, αμετάβλητη τιμή δηλώνεται ως εξής

REAL, PARAMETER :: pi = 3.14159

===== 1.5 Εντολή ανάθεσης =====

Είναι της μορφής
μεταβλητή = [ έκφραση με σταθερές και μεταβλητές ]

Εκτελούνται όλες οι πράξεις που πιθανόν εμφανίζονται στο δεξί μέλος και το αποτέλεσμα μετατρέπεται στον τύπο της μεταβλητής του αριστερού μέλους και η τιμή του αποδίδεται σε αυτή.

===== 1.6 Αριθμητικοί Τελεστές =====

Οι τελεστές + , - ,*,/ μεταξύ πραγματικών αριθμών εκτελούν τις πράξεις της πρόσθεσης, αφαίρεσης, πολλαπλασιασμού και διαίρεσης αντίστοιχα.

Οι τελεστές + , - ,* μεταξύ ακεραίων αριθμών εκτελούν τις πράξεις της πρόσθεσης, αφαίρεσης, πολλαπλασιασμού αντίστοιχα. Ο τελεστής / μεταξύ ακεραίων αριθμών εκτελεί τη διαίρεση και αποκόπτει το δεκαδικό μέρος του αποτελέσματος, επιστρέφει, δηλαδή, το πηλίκο της διαίρεσης. Η συνάρτηση MOD( ) επιστρέφει το υπόλοιπο της διαίρεσης ως εξής

INTEGER :: a, b, p, y

a = 4
b = 3
p = a / b ! Piliko
y = MOD(a,b) ! Ypoloipo

Ο τελεστής ** εκτελεί την ύψωση σε δύναμη. Π.χ. το x3 των μαθηματικών γράφεται στη Fortran ως x**3 .

Οι τελεστές + , - έχουν ίδια προτεραιότητα η οποία είναι χαμηλότερη από την προτεραιότητα των *,/ (που έχουν την ίδια), όπως και στα μαθηματικά. Σε μια έκφραση που εμφανίζονται διαδοχικά τελεστές ίδιας προτεραιότητας, εκτελείται πρώτα αυτός που είναι στα αριστερά. Ο τελεστής ** έχει υψηλότερη προτεραιότητα από όλους. Π.χ.

INTEGER :: i, j, k

i = 2 + 3 ! i = 5
j = 2 * 3 ! j = 6
i = i + j ! i = 11
i = 2 * i + j ! i = 28
i = 17 / j ! i = 2
k = i * j / 3 ! k = 4

Αλλαγή προτεραιότητας γίνεται με τη χρήση παρενθέσεων. Π.χ.

INTEGER :: i, j, k

i = 2
j = 6
k = 2 * i + j ! k = 10
k = 2 * (i + j) ! k = 16
k = j * i / 3 ! k = 4
k = j * (i / 3) ! k = 0

Παρατήρηση: Όταν οι τελεστές + , - ,*,/ εμφανίζονται μεταξύ αριθμών διαφορετικού τύπου (π.χ. ενός ακεραίου και ενός πραγματικού), γίνεται αυτόματη μετατροπή του ενός τύπου στον άλλο (για να μην έχουμε απώλεια ακρίβειας) και εκτελούνται οι πράξεις. Π.χ.

INTEGER :: i, j
REAL :: x, y

i = 8.3 ! i = 8
j = 2
y = 3.0

x = 4.0 / 3 ! -> x = 4.0 / 3.0 -> x = 1.33333

x = j + y
! -> x = 2 + 3.0 -> x = 2.0 + 3.0 -> x = 5.0

i = 4 / 3.0 ! -> i = 4.0 / 3.0 -> i = 1.33333 -> i = 1

x = 4 / 3 ! -> x = 1 -> x = 1.0

x = 2.0 * 4 / 3
! -> x = 8.0 / 3 -> x = 8.0 / 3.0 -> x = 2.66667

x = 4 / 3 * 2.0 ! -> x = 1 * 2.0 -> x = 2.0

===== 1.7 Είσοδος/Έξοδος δεδομένων =====

Η ανάθεση τιμής σε κάποια μεταβλητή a από το πληκτρολόγιο γίνεται, όπως είδαμε, με την εντολή READ :

READ *, a

ή, ισοδύναμα, με την

READ (*,*) a

Η εκτύπωση στην οθόνη της τιμής μιας ποσότητας γίνεται με την εντολή PRINT

PRINT *, a

ή, ισοδύναμα και πιο γενικά, με την εντολή WRITE :

WRITE (*,*) a

Οι παραπάνω εντολές μπορούν να χρησιμοποιηθούν για πολλά δεδομένα ταυτόχρονα, ακόμα και διαφορετικού τύπου. Π.χ.

INTEGER :: a
REAL :: b

a = 4
b = 4.5

PRINT *, "Oi times einai", a, b

Το πρώτο * στα READ(*,*)/WRITE(*,*) προσδιορίζει το πληκτρολόγιο ή την οθόνη ως πηγή ή αποδέκτη δεδομένων αντίστοιχα. Σε επόμενο κεφάλαιο θα δούμε πώς συνδέουμε αρχεία για την είσοδο και έξοδο δεδομένων. Το δεύτερο * καθορίζει ότι η μορφή των δεδομένων θα αποφασιστεί από τον μεταγλωττιστή με βάση τις ποσότητες που περιλαμβάνονται στις αντίστοιχες εντολές. Στη μορφή READ */PRINT * η μεταφορά των δεδομένων γίνεται μόνο από το πληκτρολόγιο και την οθόνη αντίστοιχα, χωρίς δυνατότητα αλλαγής, ενώ το * καθορίζει τη διαμόρφωση (και υποδηλώνει ότι αυτή θα αποφασιστεί από το μεταγλωττιστή).

===== 1.8 Δομή Προγράμματος =====

Όπως αναφέραμε, οι εντολές του κυρίως προγράμματός μας περιλαμβάνονται μεταξύ των PROGRAM ... END PROGRAM :

PROGRAM onoma

IMPLICIT NONE
...
...
...
END PROGRAM onoma

Το όνομα του προγράμματος (εδώ “onoma” ) πρέπει να εμφανίζεται, είναι της επιλογής του προγραμματιστή και σχηματίζεται με τους κανόνες που ισχύουν για τα ονόματα των μεταβλητών. Όλες οι γραμμές μπορούν να ξεκινούν από οποιαδήποτε στήλη. Σε ένα αρχείο με κώδικα σε Fortran 90, επιτρέπονται κενά μεταξύ λέξεων και κενές γραμμές μεταξύ εντολών. Στην περίπτωση που κάποια γραμμή είναι πολύ μεγάλη, πάνω από 132 χαρακτήρες ή από το πλάτος της οθόνης μας, μπορούμε να την “σπάσουμε” σε οποιοδήποτε σημείο και να συνεχιστεί στην επόμενη γραμμή του αρχείου, αρκεί στο σημείο του “σπασίματος” στην πρώτη γραμμή να τοποθετήσουμε τον χαρακτήρα `&'. Π.χ. η ακόλουθη εντολή

INTEGER :: a, b

μπορεί να γίνει, αν το επιθυμούμε,

INTEGER :: a, &
b

Με άλλα λόγια, ο χαρακτήρας `&' στο τέλος μιας γραμμής υποδηλώνει ότι αυτή συνεχίζεται στην επόμενη (και αποτελούν μία εντολή).

Οι πρώτες γραμμές μετά το PROGRAM onoma συγκεντρώνουν τις δηλώσεις όλων των ποσοτήτων. Η πρώτη γραμμή από αυτές (καλό είναι να) είναι η IMPLICIT NONE . Αφού τελειώσουν οι δηλώσεις, ακολουθούν οι υπόλοιπες εντολές, με τη σειρά που θέλουμε να εκτελεστούν.

Πριν ή μετά το κυρίως πρόγραμμα μπορούμε να έχουμε ορισμούς συναρτήσεων, υπορουτινών και modules. Αυτοί εκτελούνται μόνο αν χρησιμοποιηθούν από το κυρίως πρόγραμμα.

''1.8.1 STOP''

Το πρόγραμμα τελειώνει την εκτέλεσή του κανονικά όταν συναντήσει το END PROGRAM . Ενναλακτικά, μπορούμε να δώσουμε την εντολή STOP σε οποιοδήποτε σημείο του. Η εκτέλεσή της προκαλεί την διακοπή του προγράμματος και εκτύπωνεται σχετικό μήνυμα. Το STOP μπορεί να ακολουθείται από ένα σύνολο χαρακτήρων εντός εισαγωγικών, απλών ή διπλών, ή ένα ακέραιο αριθμό με έως 5 ψηφία. Σε τέτοια περίπτωση, το πρόγραμμα κατά τη διακοπή μπορεί να εκτυπώσει ό,τι ακολουθεί το STOP . Με αυτόν τον τρόπο μπορούμε να διακρίνουμε ποιο STOP προκάλεσε τη διακοπή.

2.

===== 2.1 Άλλοι ενσωματωμένοι τύποι =====

Παρακάτω θα παρουσιάσουμε τους υπόλοιπους ενσωματωμένους τύπους που παρέχει η Fortran, πέρα από τους INTEGER , REAL , DOUBLE PRECISION .

''2.1.1 Τύπος χαρακτήρα''
Για την αναπαράσταση ποσοτήτων που οι δυνατές τιμές τους είναι χαρακτήρες ή σειρές χαρακτήρων, η Fortran παρέχει τον τύπο CHARACTER . Δήλωση μιας μεταβλητής χαρακτήρα με όνομα π.χ. ch γίνεται με την ακόλουθη εντολή:

CHARACTER :: ch

Οι τιμές που μπορεί να πάρει είναι μονοί χαρακτήρες εντός απλών (') ή διπλών (") εισαγωγικών:

ch = 'a'
ch = 'D'
ch = "R"

Για την περιγραφή σειράς χαρακτήρων, η δήλωση περιλαμβάνει το μέγιστο δυνατό μήκος των σειρών που πρόκειται να αναπαραστήσουμε. Για παράδειγμα, η σειρά "This is a message" αποτελείται από 17 χαρακτήρες και, επομένως, μπορεί να ανατεθεί σε μεταβλητή τύπου CHARACTER με τουλάχιστον αυτό το μήκος. Η κατάλληλη δήλωση είναι

CHARACTER (17) :: ch

ch = "This is a message"

''2.1.2 Λογικός τύπος''

Μεταβλητή λογικού τύπου ( LOGICAL ) είναι κατάλληλη για την αναπαράσταση ποσοτήτων που μπορούν να πάρουν δύο τιμές (π.χ. ναι/όχι, αληθές/ψευδές,...). Η δήλωση τέτοιας μεταβλητής, με όνομα π.χ. a, γίνεται ως εξής:

LOGICAL :: a

Οι τιμές που μπορεί να πάρει είναι .TRUE. ή .FALSE. . Η ανάθεση π.χ. της σταθερής τιμής .TRUE. στην a γίνεται ως εξής

a = .TRUE.

''2.1.3 Μιγαδικός Τύπος''

Η Fortran υποστηρίζει με τον ενσωματωμένο τύπο COMPLEX την περιγραφή μιγαδικών μεταβλητών και σταθερών. Η δήλωση είναι ως εξής

COMPLEX :: z

Εσωτερικά, για τον compiler, αποτελείται από δύο πραγματικούς αριθμούς που αντιστοιχούν στο πραγματικό και φανταστικό μέρος.

''2.1.3.1 Δημιουργία μιγαδικής ποσότητας''

Ένας αριθμός μιγαδικού τύπου στη Fortran μπορεί να πάρει τιμή
με ανάθεση μιγαδικής σταθεράς, δηλαδή, δύο πραγματικών αριθμών σε ένα ζεύγος παρενθέσεων, με (,) μεταξύ τους. Ο πρώτος αριθμός είναι το πραγματικό μέρος ενώ ο δεύτερος είναι το φανταστικό:

COMPLEX :: z

z = (1.2, 4.56) ! z = 1.2 + i 4.56

Αν ανατεθεί πραγματική ή ακέραια ποσότητα θα γίνει μετατροπή συμπληρώνοντας με το 0 το φανταστικό μέρος·

COMPLEX :: z

z = 2 ! z = 2.0 + i 0.0
z = 2.3 ! z = 2.3 + i 0.0

Με ανάθεση έκφρασης που έχει συνολικά μιγαδική τιμή (ή πραγματική ή ακέραια, για τις οποίες θα προηγηθεί μετατροπή):

COMPLEX :: z, z1, z2

z1 = 3 ! z1 = 3.0 + i 0.0
z2 = (4.6, -4.2) ! z2 = 4.6 - i 4.2

z = z1 + z2 + 5.1 ! z = 12.7 - i 4.2

Στην επόμενη παράγραφο θα δούμε περισσότερα για τις εκφράσεις.
Με ανάγνωση μιγαδικής σταθεράς από το πληκτρολόγιο ή από αρχείο. Π.χ. ο κώδικας

COMPLEX :: z

READ *, z

αναμένει να του δώσουμε
(1.6, -3.8 )
(περιλαμβάνονται το κόμμα και οι παρενθέσεις).
Με τη χρήση της ενσωματωμένης συνάρτησης CMPLX( ) . Αυτή δέχεται δύο πραγματικές ποσότητες και δημιουργεί μιγαδικό αριθμό με πραγματικό μέρος την πρώτη ποσότητα και φανταστικό τη δεύτερη

COMPLEX :: z
REAL :: x, y

x = 3.4
y = -9.1

z = CMPLX(x,y) ! z = 3.4 -i 9.1

Προσέξτε ότι η έκφραση

z = (x,y)

που ίσως να περίμενε κανείς ότι θα έκανε σωστά τη δημιουργία είναι λάθος.

''2.1.3.2 Εκφράσεις με μιγαδικές ποσότητες''

Οι αριθμητικοί τελεστές + , - ,*,/,** εκτελούν τις αναμενόμενες πράξεις από τα μαθηματικά· ο τελευταίος συμβολίζει την ύψωση σε δύναμη. Υπενθυμίζουμε ότι αν

z1 = α + iβ, z2 = γ + iδ
τότε
z1 z2 = (αγ-βδ) + i(αδ+βγ)
Αυτήν ακριβώς την πράξη εκτελεί ο τελεστής * μεταξύ μιγαδικών ποσοτήτων στη Fortran. Αντίστοιχα ισχύουν και για τον τελεστή / .

Κατ' επέκταση όσων είπαμε στις §1.5, §1.6, ένας μιγαδικός που συμμετέχει σε μία πράξη (δεξιά ή αριστερά ενός τελεστή), προκαλεί τη μετατροπή σε μιγαδικό και του άλλου μέρους της πράξης, προτού αυτή εκτελεστεί. Επίσης, ανάθεση του μιγαδικού αποτελέσματος μιας έκφρασης προκαλεί μετατροπή της τιμής στον τύπο της μεταβλητής στην οποία ανατίθεται. Όταν η μεταβλητή στην οποία ανατίθεται είναι πραγματική, η μετατροπή συνίσταται στην αποκοπή του φανταστικού τμήματος:

COMPLEX :: z
REAL :: x

z = (2.3,4.5) ! z = 2.3 + i 4.5

x = z ! x = 2.3

Όταν η μεταβλητή στην οποία ανατίθεται είναι ακέραια, η μετατροπή που γίνεται είναι η αποκοπή του φανταστικού τμήματος και η αποκοπή του δεκαδικού τμήματος του πραγματικού μέρους:

COMPLEX :: z
INTEGER :: i

z = (2.3,4.5) ! z = 2.3 + i 4.5

i = z ! i = 2

Όλες οι ενσωματωμένες συναρτήσεις που θα δούμε παρακάτω ισχύουν και για μιγαδικούς με την αναμενόμενη από τα μαθηματικά συμπεριφορά.

''2.1.3.3 Εξαγωγή πραγματικού/φανταστικού μέρους --Συζυγής--Μέτρο''

Το πραγματικό μέρος ενός μιγαδικού αριθμού υπολογίζεται με τη δράση της ενσωματωμένης συνάρτησης REAL( ) βάζοντας ως όρισμα (ποσότητα μεταξύ των παρενθέσεων) τον μιγαδικό αριθμό:

COMPLEX :: z
REAL :: x

z = (1.2,-8.7) ! z = 1.2 -i 8.7
x = REAL(z) ! x = 1.2

Το φανταστικό μέρος εξάγεται με τη δράση της συνάρτησης AIMAG( ) :

COMPLEX :: z
REAL :: x

z = (1.2,-8.7) ! z = 1.2 -i 8.7
x = AIMAG(z) ! x = -8.7

Ο μιγαδικός συζυγής ενός αριθμού παράγεται με τη δράση της συνάρτησης CONJG( ) :

COMPLEX :: z1, z2

z1 = (1.2,-8.7) ! z1 = 1.2 - i 8.7
z2 = CONJG(z1) ! z2 = 1.2 + i 8.7

Το (πραγματικό) μέτρο | z| ενός μιγαδικού υπολογίζεται με τη συνάρτηση ABS( ) :

COMPLEX :: z
REAL :: norm

z = (1.2,-8.7) ! z = 1.2 - i 8.7
norm = ABS(z) ! norm = (1.2**2 + 8.7**2)**0.5

''2.1.4 Ρητή μετατροπή μεταξύ αριθμητικών τύπων''

Υπάρχουν περιπτώσεις που χρειάζεται να κάνουμε ρητά μετατροπή ποσότητας από ένα τύπο σε άλλον και να μη βασιζόμαστε στη μετατροπή που γίνεται κατά την ανάθεση. Π.χ. προσέξτε τον παρακάτω κώδικα

REAL :: x
INTEGER :: i, j

i = 3
j = 2

x = i/j ! x = 1.0

Η μεταβλητή x δεν αποκτά την επιθυμητή τιμή (1.5 ) καθώς της ανατίθεται το πηλίκο της διαίρεσης των δύο ακεραίων αριθμών. Η διαίρεση θα γίνει “σωστά” αν μετατρέψουμε τους ακεραίους που συμμετέχουν σε πραγματικούς με κλήση της κατάλληλης συνάρτησης

REAL :: x
INTEGER :: i, j

i = 3
j = 2

x = real(i)/real(j) ! x = 1.5

Η ρητή μετατροπή αρκεί να γίνει στον ένα από τους δύο· ο άλλος θα μετατραπεί αυτόματα στον τύπο του έτερου μέλους.

Οι συναρτήσεις ρητής μετατροπής της Fortran είναι οι εξής: REAL( ), DBLE( ) , INT( ) , CMPLX( ) . Δέχονται μεταξύ των παρενθέσεων μία ποσότητα οποιουδήποτε αριθμητικού τύπου ( INTEGER , REAL , DOUBLE PRECISION , COMPLEX ) και επιστρέφουν αντίστοιχα: REAL , DOUBLE PRECISION , INTEGER , COMPLEX . Η μετατροπή γίνεται σύμφωνα με όσα έχουμε αναφέρει για την ανάθεση στους ομώνυμους τύπους.

===== 2.2 Ενσωματωμένες αριθμητικές συναρτήσεις =====

Εκτός από αυτές ( AIMAG( ) , CONJG( ) , CMPLX( ) ) που παρουσιάσαμε στην §2.1.3, η Fortran παρέχει μια πληθώρα αριθμητικών συναρτήσεων. Οι πιο χρήσιμες παρουσιάζονται στον Πίνακα 2.1.

Πίνακας: Ενσωματωμένες συναρτήσεις της Fortran.
''Συνάρτηση'' ''Επιστρεφόμενη τιμή'' ''Παρατηρήσεις''
abs(x) Απόλυτη τιμή του ορίσματος.
ceiling(x)Η αμέσως μεγαλύτερη από το όρισμα ακέραια τιμή.
floor(x) Η αμέσως μικρότερη από το όρισμα ακέραια τιμή.
int(x) Το ακέραιο μέρος του ορίσματος ως ακέραιος.
nint(x) Η πλησιέστερη στο όρισμα ακέραια τιμή ως ακέραιος.
aint(x) Το ακέραιο μέρος του ορίσματος ως πραγματικός.
anint(x) Η πλησιέστερη στο όρισμα ακέραια τιμή ως πραγματικός.
real(x) Το όρισμα ως πραγματικός.
dble(x) Το όρισμα ως πραγματικός διπλής ακρίβειας.
sqrt(x) Η τετραγωνική ρίζα του ορίσματος. Το όρισμα πρέπει να είναι μη αρνητικό.
cos(x) Συνημίτονο. Το όρισμα σε rad.
sin(x) Ημίτονο. Το όρισμα σε rad.
tan(x) Εφαπτομένη. Το όρισμα σε rad.
acos(x) Τόξο συνημιτόνου. Το όρισμα στο [- 1 : 1] , το αποτέλεσμα στο [0 : π] σε rad.
asin(x) Τόξο ημιτόνου. Το όρισμα στο [- 1 : 1] , το αποτέλεσμα στο [- π/2 : π/2] σε rad.
atan(x) Τόξο εφαπτομένης. Το αποτέλεσμα στο [- π/2 : π/2] σε rad.
atan2(x, y)Τόξο εφαπτομένης arctan(x/y) . Τα πρόσημα των ορισμάτων καθορίζουν το τεταρτημόριο.
Το αποτέλεσμα στο (- π : π] σε rad.
cosh(x) Υπερβολικό συνημίτονο.
sinh(x) Υπερβολικό ημίτονο.
tanh(x) Υπερβολική εφαπτομένη.
exp(x) Εκθετικό.
log(x) Φυσικός λογάριθμος. Πρέπει το όρισμα να είναι θετικό.
log10(x) Δεκαδικός λογάριθμος. Πρέπει το όρισμα να είναι θετικό.
mod(x,y) Το υπόλοιπο της διαίρεσης x/y . Επιστρέφει το a-int(a/p)*p . Τα a,p ίδιου τύπου, ακέραιου ή πραγματικού.
max(x1,x2,...) Ο μεγαλύτερος από τα δύο ή περισσότερα ορίσματα. Τα ορίσματα πρέπει να είναι ίδιου τύπου.
min(x1,x2,...) Ο μικρότερος από τα δύο ή περισσότερα ορίσματα. Τα ορίσματα πρέπει να είναι ίδιου τύπου.
dim(x,y) max(x-y,0.0) . Τα ορίσματα πρέπει να είναι ίδιου τύπου, ακέραιου ή πραγματικού.

Προσέξτε ότι υπάρχουν συναρτήσεις που δέχονται πραγματικό όρισμα και επιστρέφουν αριθμό με τον ίδιο τύπο με το όρισμα (πραγματικό) και ίδιας ακρίβειας. Αν, επομένως, δεχθούν όρισμα DOUBLE PRECISION επιστρέφουν DOUBLE PRECISION .

2.2.0.0.1 Παραδείγματα:
Ένα πρόγραμμα Fortran που υπολογίζει και τυπώνει την τετραγωνική ρίζα ενός πραγματικού αριθμού που εισάγεται από τον χρήστη είναι το ακόλουθο:

PROGRAM riza

IMPLICIT NONE
REAL :: x

PRINT *, "Dwse ena pragmatiko"
READ (*,*) x

PRINT *, "H tet. riza einai"
WRITE (*,*) SQRT(x)

END PROGRAM riza

Ο υπολογισμός του ημιτόνου της γωνίας που δίνει ο χρήστης μπορεί να γίνει με το εξής πρόγραμμα

PROGRAM imitono

IMPLICIT NONE
REAL :: theta, x

PRINT *, "Dwse ena pragmatiko"
READ (*,*) theta

x = SIN(theta)

PRINT *, ";To imitono einai"
WRITE (*,*) x

END PROGRAM imitono

Εντός των παρενθέσεων που ακολουθούν το όνομα της συνάρτησης μπορεί να εμφανίζεται μία σταθερά ή μεταβλητή ή οποιαδήποτε πολύπλοκη έκφραση (που μπορεί να περιλαμβάνει άλλες συναρτήσεις), αρκεί να μπορεί να μετατραπεί στον κατάλληλο τύπο για το όρισμα, όπως αυτός προσδιορίζεται στον Πίνακα 2.1.

Προσέξτε ότι οι τριγωνομετρικές συναρτήσεις δέχονται όρισμα εκφρασμένο σε ακτίνια (rad).

===== 2.3 Σχεσιακοί και Λογικοί Τελεστές =====

Η Fortran υποστηρίζει τη σύγκριση αριθμητικών ποσοτήτων (ή ποσοτήτων τύπου χαρακτήρα) με τη βοήθεια των σχεσιακών τελεστών (Πίνακας 2.2).

Σχεσιακοί τελεστές στη Fortran.
== ίσο
/= άνισο
> μεγαλύτερο
< μικρότερο
>= μεγαλύτερο ή ίσο
<= μικρότερο ή ίσο

Το αποτέλεσμα της σύγκρισης είναι λογική ποσότητα και επομένως έχει τιμή .TRUE. ή .FALSE. . Π.χ. το 3.0>2.0 είναι .TRUE. ενώ το 2/=1+1 είναι .FALSE. . Οι αριθμητικοί τελεστές έχουν μεγαλύτερη προτεραιότητα από τους σχεσιακούς. Προσέξτε ότι, όπως και στα μαθηματικά, μόνο οι τελεστές ==,/= έχουν νόημα για μιγαδικές ποσότητες.

Για τη σύνδεση λογικών εκφράσεων η Fortran παρέχει τους λογικούς τελεστές .NOT. , .AND. , .OR. , .EQV. , .NEQV. . Όλοι εκτός από τον πρώτο, δρουν μεταξύ δύο λογικών ποσοτήτων ή εκφράσεων και σχηματίζουν μια νέα λογική ποσότητα:
Ο τελεστής .NOT. δρα στη λογική έκφραση που τον ακολουθεί και της αλλάζει την τιμή:
Το .NOT. (4 > 3) είναι .FALSE.
Το .NOT. (4 < 3) είναι .TRUE.
Η λογική έκφραση που σχηματίζεται συνδέοντας δύο άλλες εκφράσεις με τον τελεστή .AND. έχει τιμή .TRUE. μόνο αν και οι δύο ποσότητες είναι .TRUE. . Σε άλλη περίπτωση είναι .FALSE. :
Το (4 > 3) .AND. (3.0 > 2.0) είναι .TRUE.
Το (4 < 3) .AND. (3.0 > 2.0) είναι .FALSE.
Ο τελεστής .OR. μεταξύ δύο λογικών εκφράσεων σχηματίζει μια νέα ποσότητα με τιμή .TRUE. αν έστω και μία από τις δύο ποσότητες είναι .TRUE. , αλλιώς είναι .FALSE. :
Το (4 > 3) .OR. (3.0 < 2.0) είναι .TRUE.
Το (4 < 3) .OR. (3.0 < 2.0) είναι .FALSE.
Η λογική έκφραση που σχηματίζεται συνδέοντας δύο άλλες εκφράσεις με τον τελεστή .EQV. έχει τιμή .TRUE. αν οι δύο ποσότητες έχουν την ίδια τιμή· σε άλλη περίπτωση είναι .FALSE. :
Το (4 < 3) .EQV. (3.0 < 2.0) είναι .TRUE.
Το (4 < 3) .EQV. (3.0 > 2.0) είναι .FALSE.
Ο τελεστής .NEQV. μεταξύ λογικών εκφράσεων σχηματίζει ποσότητα με τιμή .TRUE. αν οι δύο ποσότητες έχουν διαφορετική τιμή· αλλιώς είναι .FALSE. :
Το (4 < 3) .NEQV. (3.0 < 2.0) είναι .FALSE.
Το (4 < 3) .NEQV. (3.0 > 2.0) είναι .TRUE.
Παρατηρείστε ότι οι δύο τελευταίοι τελεστές, .EQV. και .NEQV. , ουσιαστικά ελέγχουν την ισότητα ή ανισότητα λογικών ποσοτήτων.

Μια έκφραση με σχεσιακούς ή λογικούς τελεστές της Fortran μπορεί να ανατεθεί σε μεταβλητές τύπου LOGICAL :

LOGICAL :: a, b
INTEGER :: i, max

...

a = 3==2
b = ( (i > 0) .AND. (i < max) )

===== 2.4 Εντολές Ελέγχου =====

''2.4.1 IF''

Η εντολή IF είναι μία από τις βασικές δομές διακλάδωσης κάθε γλώσσας προγραμματισμού. Ελέγχει τη ροή του κώδικα ανάλογα με τη (λογική) τιμή μιας συνθήκης, δηλαδή μιας ποσότητας ή έκφρασης λογικού τύπου. Στη Fortran συντάσσεται ως εξής:

IF (condition) THEN
...
... // block A
ELSE
...
... // block B
END IF

Εάν η συνθήκη (condition), είναι αληθής, εκτελούνται οι εντολές του τμήματος Α. Αν η συνθήκη είναι ψευδής, τότε εκτελείται το block των εντολών μεταξύ του ELSE και του END IF (τμήμα Β).

Το κάθε τμήμα μπορεί να αποτελείται από καμμία, μία, ή περισσότερες εντολές. Π.χ.

IF (val > 0.0) THEN
PRINT *, "The number is positive"
max = val
ELSE
PRINT *, "The number is not positive"
END IF

Το κάθε τμήμα μπορεί να περιλαμβάνει οποιεσδήποτε άλλες εντολές και βέβαια άλλο IF . Παρατηρήστε ότι η στοίχιση των εντολών υποδηλώνει ότι βρίσκονται στο “εσωτερικό” μιας (σύνθετης) εντολής. Δεν είναι υποχρεωτική αλλά διευκολύνει τον προγραμματιστή στην κατανόηση του κώδικα.

Στην περίπτωση που το τμήμα μετά το ELSE είναι κενό, η λέξη ELSE μπορεί να παραληφθεί:

IF (condition) THEN
...
...
END IF

Εναλλακτική σύνταξη της περίπτωσης χωρίς τμήμα ELSE και με μία μόνο εντολή στο σώμα του IF , δηλαδή της

IF (condition) THEN
"command"
END IF

είναι η ακόλουθη:

IF (condition) "command"

Παρατηρήστε ότι παραλείπονται τα THEN, END IF .

Αναφέραμε ότι στο σώμα ενός IF μπορεί να περιέχεται άλλο IF , π.χ.

IF (cond1) THEN
...
ELSE
IF (cond2) THEN
...
ELSE
...
END IF
END IF

Συντετμημένη μορφή της παραπάνω δομής είναι η

IF (cond1) THEN
...
ELSE IF (cond2) THEN
...
ELSE
...
END IF

Η δεύτερη μορφή δυσκολεύει την κατανόηση του κώδικα και γι' αυτό είναι καλό να αποφεύγεται. Σε πολλές περιπτώσεις αυτής της μορφής, η χρήση της δομής SELECT CASE (§2.4.2) είναι προτιμότερη.

''2.4.2 SELECT CASE''

Η δομή SELECT CASE αποτελεί μια πιο κομψή και κατανοητή εκδοχή πολλαπλών εσωκλειόμενων ή διαδοχικών IF . Η σύνταξή της είναι:

SELECT CASE (i)
CASE (value1)
...
CASE (value2)
...
...
...
CASE (valueN)
...
CASE default
...
END SELECT

Η τιμή ελέγχου i πρέπει να είναι ακέραιου τύπου (ή λογικού ή χαρακτήρα). Οι τιμές value1, value2, ...,valueN πρέπει να είναι σταθερές αντίστοιχου τύπου και διακριτές μεταξύ τους (να μην επαναλαμβάνονται).

Κατά την εκτέλεση, η τιμή ελέγχου i συγκρίνεται με κάθε μία από τις value1, value2,...,valueN. Αν η τιμή της περιλαμβάνεται σε αυτές, τότε εκτελούνται οι εντολές που ακολουθούν το αντίστοιχο CASE . Όταν αυτές εξαντληθούν, η εκτέλεση συνεχίζει με την πρώτη εντολή μετά το END SELECT . Αν δεν βρεθεί η τιμή της στις value1, value2,...,valueN εκτελείται το τμήμα των εντολών του default , αν υπάρχει. Αλλιώς, η εκτέλεση συνεχίζει μετά το END SELECT .

Οι σχετικές θέσεις των CASE μπορούν να είναι οποιεσδήποτε.

2.4.2.0.1 Παράδειγμα:
Έστω ότι σε ένα παιχνίδι θέλουμε να αντιστοιχήσουμε ένα αριθμό που θα πληκτρολογεί ο χρήστης με την κατεύθυνση στην οποία θα κινηθεί ο ήρωας του παιχνιδιού. Έστω ακόμη ότι η συσχέτιση είναι 0→ “επάνω” , 1→ “κάτω” , 2→ “αριστερά” , 3→ “δεξιά” , ενώ για άλλη τιμή να μένει ακίνητος. Μπορούμε να γράψουμε τον εξής κώδικα

INTEGER :: i
CHARACTER (5) :: direction

READ *, i

SELECT CASE (i)
CASE (0)
direction = "up"
CASE (1)
direction = "down"
CASE (2)
direction = "left"
CASE (3)
direction = "right"
CASE default
direction = "none"
END SELECT

PRINT *, "The direction is ", direction

Όταν επιθυμούμε να εκτελεστούν οι ίδιες εντολές για πολλές τιμές του i, μπορούμε να τις ομαδοποιήσουμε. Π.χ. έστω ότι οι αριθμοί (0, 4, Cool είναι ισοδύναμοι και αντίστοιχα οι (1, 5, 9) , (2, 6) και (3, 7) . Ο κώδικας δεν επαναλαμβάνεται αν τον τροποποιήσουμε ως εξής

SELECT CASE (i)
CASE (0,4,Cool
direction = "up"
CASE (1,5,9)
direction = "down"
CASE (2,6)
direction = "left"
CASE (3,7)
direction = "right"
CASE default
direction = "none"
END SELECT

Τέλος, στην περίπτωση που έχουμε συνεχόμενες επιθυμητές τιμές, μπορούμε να τις δώσουμε με τη μορφή
χαμηλή τιμή : υψηλή τιμή
Έτσι, ένα CASE με τη μορφή

CASE (0:9)
...

εκτελείται όταν η τιμή ελέγχου είναι στο διάστημα [0, 9] . Αν παραληφθεί το ένα από τα δύο όρια θεωρείται ότι έχουμε βάλει το μικρότερο ή μεγαλύτερο (ανάλογα) αριθμό της Fortran. Π.χ.

SELECT CASE (i)
CASE (:-1)
... ! commands for i < 0
CASE (0)
... ! commands for i == 0
CASE (1:)
... ! commands for i > 0
END SELECT

==== 3. Πίνακες-Εντολές επανάληψης ====

Χρησιμοποιώντας τους θεμελιώδεις, ενσωματωμένους τύπους που είδαμε ως τώρα, μπορούμε να ορίσουμε άλλους, σύνθετους, με κατάλληλο τρόπο ώστε να αναπαριστούν έννοιες του προβλήματός μας. Έτσι για ομάδα σχετιζόμενων ποσοτήτων ίδιου τύπου χρησιμοποιούμε πίνακα (array) ενώ για σύνολο μεταβλητών διαφορετικού (ή και ίδιου) τύπου κατάλληλη είναι η δομή TYPE που θα περιγράψουμε σε επόμενο κεφάλαιο.

===== 3.1 Πίνακας =====

Συχνά υπάρχει η ανάγκη να χρησιμοποιήσουμε ένα σύνολο σχετιζόμενων ποσοτήτων με ίδιο τύπο. Για παράδειγμα, η θερμοκρασία ενός τόπου σε μια συγκεκριμένη ημέρα μπορεί να αναπαρασταθεί με μια πραγματική μεταβλητή· τι κάνουμε όμως αν επιθυμούμε να αποθηκεύσουμε τις θερμοκρασίες ενός μήνα ή ενός έτους; Θα μπορούσαμε να δηλώσουμε και να χρησιμοποιήσουμε 30 ή 365 μεταβλητές, αντιλαμβανόμαστε όμως ότι είναι επίπονη διαδικασία και υπάρχει μεγάλη πιθανότητα για λάθη. H Fortran, όπως και όλες οι γλώσσες προγραμματισμού, μας δίνει τη δυνατότητα να τις ορίσουμε όλες μαζί με μία εντολή. Έτσι, ένα σύνολο 30 πραγματικών μεταβλητών το αναπαριστούμε με έναν πραγματικό πίνακα, μονοδιάστατο, 30 θέσεων. Η δήλωση τέτοιου πίνακα με όνομα π.χ. temperature γίνεται ως εξής

REAL :: temperature(30)

ή, ισοδύναμα,

REAL, DIMENSION (30) :: temperature

Η δήλωση ενός πίνακα μπορεί να γίνει ταυτόχρονα με άλλες, απλές, μεταβλητές ή και άλλους πίνακες. Π.χ. η εντολή

REAL :: a, b(3), c(10)

δημιουργεί μια απλή μεταβλητή με όνομα a, ένα πίνακα 3 στοιχείων, τον b, και έναν άλλο πίνακα 10 θέσεων, τον c, όλα πραγματικά. Προσέξτε όμως, στον ακόλουθο κώδικα, την εφαρμογή του δεύτερου τρόπου δήλωσης που είδαμε πιο πάνω:

INTEGER, DIMENSION (10) :: a, b, c, d(4)

Με την παραπάνω εντολή δημιουργούμε 4 μονοδιάστατους πίνακες ακεραίων, τους τρεις (a, b, c) με 10 στοιχεία και τον τέταρτο (d) με 4 . Στις δηλώσεις πινάκων το πλήθος των στοιχείων πρέπει να είναι ακέραια σταθερή ποσότητα, γνωστή κατά τη μεταγλώττιση. Αυτό σημαίνει ότι εκτός από ακέραιο αριθμό για τη διάσταση, μπορούμε να χρησιμοποιήσουμε ποσότητα που έχει την ιδιότητα PARAMETER , §1.4.

''3.1.1 Πρόσβαση Στοιχείων''

Η πρόσβαση σε κάθε στοιχείο του μονοδιάστατου πίνακα γίνεται χρησιμοποιώντας το όνομα του πίνακα ακολουθούμενο από ένα ακέραιο αριθμό (ή έκφραση που δίνει ακέραιο) μέσα σε παρενθέσεις, από 1 έως το πλήθος των στοιχείων του πίνακα3.1. Έτσι, το πρώτο στοιχείο είναι το temperature(1) , το δεύτερο είναι το temperature(2) , ενώ το τελευταίο είναι το temperature(30) . Με αυτά τα “ονόματα” συμμετέχουν σε εκφράσεις. Π.χ., o μέσος όρος των 3 πρώτων στοιχείων του temperature είναι ο

REAL :: mo3

mo3 = (temperature(1) + temperature(2) + temperature(3)) / 3

''3.1.2 Απόδοση τιμής''

Απόδοση τιμής σε στοιχείο πίνακα γίνεται με τους δύο γνωστούς τρόπους: της εντολής ανάθεσης και της εντολής ανάγνωσης από το πληκτρολόγιο (ή αρχείο). Π.χ.

REAL :: a(5)

a(1) = 3.0
READ (*,*) a(2)

Ένας πίνακας μπορεί να πάρει τιμή ως σύνολο με τους ακόλουθους τρόπους:
δίνοντας μεταξύ (/ και /) , ποσότητες κατάλληλου τύπου για όλα τα στοιχεία του. Έτσι, ο πραγματικός μονοδιάστατος πίνακας, 5 στοιχείων, με όνομα c, αποκτά τις τιμές 3.0, 4.5, 2.0, -1.0E2, 0.99 για κάθε στοιχείο του αντίστοιχα, με την ακόλουθη εντολή ανάθεσης

REAL :: c(5)

c = (/ 3.0, 4.5, 2.0, -1.0e2, 0.99 /)

με ανάθεση άλλου πίνακα της ίδιας διάστασης και πλήθους στοιχείων (ή τμήματος πίνακα με αυτά τα χαρακτηριστικά, όπως θα δούμε παρακάτω). Π.χ.

REAL, DIMENSION (5) :: c, d

c = (/ 3.0, 4.5, 2.0, -1.0e2, 0.99 /)

d = c

με ανάγνωση από το πληκτρολόγιο ή από αρχείο, ποσοτήτων κατάλληλου πλήθους και τύπου όταν εκτελεστεί η εντολή READ για ολόκληρο τον πίνακα:

REAL, DIMENSION (5) :: c

READ *, c ! needs 5 reals

με μετατροπή άλλου πίνακα ίδιας ή διαφορετικής διάστασης χρησιμοποιώντας ενσωματωμένες συναρτήσεις ( RESHAPE( ) , SPREAD( ) , CSHIFT( )/EOSHIFT( ) , κ.λ.π.). Δεν θα επεκταθούμε περισσότερο σε αυτές.

'' 3.1.3 Πολυδιάστατος πίνακας ''

Πολύ συχνά σε επιστημονικούς κώδικες, εμφανίζεται η ανάγκη να αναπαραστήσουμε ποσότητες σε 2 ή 3 διαστάσεις, π.χ. σε ένα καρτεσιανό πλέγμα. Η Fortran παρέχει τη δυνατότητα να ορίσουμε διδιάστατους και, γενικότερα, πολυδιάστατους πίνακες (μέχρι 7 διαστάσεων). Η εντολή δήλωσης ενός πραγματικού διδιάστατου πίνακα 10 x 20 είναι η εξής

REAL :: a(10,20)

ή, ισοδύναμα,

REAL, DIMENSION (10,20) :: a

Το στοιχείο της γραμμής i και στήλης j είναι το a(i,j) . Ό,τι ισχύει για μονοδιάστατους πίνακες μπορεί να γενικευτεί και για τους πολυδιάστατους.3.2

3.1.3.0.1 Παράδειγμα:
Έστω ότι έχουμε τις θερμοκρασίες ενός τόπου για κάθε ημέρα συγκεκριμένου έτους. Αυτές μπορούν να μας δίνονται με την παρακάτω μορφή
Ημέρα Θερμοκρασία (oC)
1 5.0
2 7.5
3 6.4
1#1 1#1
157 19.1
158 21.4
1#1 1#1
364 4.5
365 7.0

Οι ημέρες αριθμούνται από το 1 έως το 365 .

Παρατηρήστε ότι για να προσδιορίσουμε μια συγκεκριμένη θερμοκρασία πρέπει να γνωρίζουμε σε ποιά γραμμή είναι, δηλ. σε ποιά ημέρα αναφερόμαστε. Με άλλα λόγια, η πληροφορία μας (οι θερμοκρασίες) παραμετροποιείται με ένα ακέραιο αριθμό. Είναι φυσιολογικό, σε πρόγραμμά μας, να αποθηκεύσουμε τις θερμοκρασίες σε ένα μονοδιάστατο πίνακα:

REAL :: tempr(365)

Εναλλακτικά, οι θερμοκρασίες μπορεί να μας δίνονται στην ακόλουθη μορφή
1 2 ... 14 15 ... 30 31
1 5.0 7.5 ... 8.3 9.2 ... 12.3 11.0
2 4.5 6.5 ... 7.0 9.0 ...
1#1 1#1 1#1 1#1 1#1 1#1 1#1
6 25.0 27.5 ... 28.3 29.2 ... 27.3
7 26.0 27.0 ... 26.0 31.0 ... 32.5 33.0
1#1 1#1 1#1 1#1 1#1 1#1 1#1
11 5.0 6.5 ... 7.6 10.0 ... 11.0
12 5.0 7.5 ... 8.5 9.5 ... 12.0 12.5

Η πρώτη γραμμή παραθέτει τις ημέρες ενός μήνα ενώ η πρώτη στήλη παραθέτει τους μήνες.

Παρατηρήστε ότι για να προσδιορίσουμε μια συγκεκριμένη θερμοκρασία πρέπει να καθορίσουμε δύο ακέραιους αριθμούς: τον μήνα (γραμμή) και την ημέρα (στήλη). Επομένως, σε ένα προγραμμά μας είναι φυσιολογικό να χρησιμοποιήσουμε για τις θερμοκρασίες ένα διδιάστατο πίνακα 12 x 31 ,

REAL :: tempr(12,31)

που θα αποθηκεύει ουσιαστικά την ίδια πληροφορία με την προηγούμενη περίπτωση. Έτσι, η θερμοκρασία στις 14 Απριλίου είναι στη θέση tempr(4,14) .3.3

''3.1.4 Παρατηρήσεις''

Ένας πίνακας μπορεί, όπως και κάθε άλλη ποσότητα, να οριστεί ως σταθερή περιλαμβάνοντας στη δήλωση την ιδιότητα PARAMETER , §1.4, με ταυτόχρονη απόδοση αρχικής (και μόνιμης) τιμής:

REAL, DIMENSION(3), PARAMETER :: coefficients = (/2.0, 2.0, 4.0/)

Υπάρχουν περιπτώσεις που το σύνολο των ποσοτήτων που αναπαριστά ο πίνακας έχει καταλληλότερη αρίθμηση, πιο “φυσική” , που ξεκινά από άλλο αριθμό και όχι το 1 . Π.χ. ένας πίνακας που αποθηκεύει τις πρώτες 6 δυνάμεις του 2 (20 , 21 , 22 ,...,25 ) είναι πολύ πιο βολικό να αριθμεί τα στοιχεία του από 0 έως 5 . Η κατάλληλη δήλωση αυτού του μονοδιάστατου πίνακα περιλαμβάνει τους αριθμούς θέσης του πρώτου και του τελευταίου στοιχείου. Π.χ.

INTEGER, PARAMETER :: powersOf2(0:5) = (/1,2,4,8,16,32/)

Με την παραπάνω δήλωση, ο πίνακας έχει πάλι 6 στοιχεία, στις θέσεις powersOf2(0) , powersOf2(1) , ..., powersOf2(5) . Γενικότερα, η δήλωση

REAL :: a(M:N)

ή, ισοδύναμα,

REAL, DIMENSION (M:N) :: a

με M,N ακέραιες σταθερές και N ≥ M, δημιουργεί το μονοδιάστατο πραγματικό πίνακα a με (N-M+1) στοιχεία, τα a(M) , a(M+1) , ..., a(N) .

Με αφορμή τη δήλωση του powersOf2, ας δούμε έναν άλλο τρόπο να δημιουργήσουμε το σταθερό πίνακα [σύνολο τιμών χωριζόμενων με (,) μεταξύ (/ , /) ] αν αυτός έχει τιμές που μπορούν να παραχθούν επαναληπτικά:

INTEGER :: a(0:5)
INTEGER :: i

a = (/ (2**i,i=0,5) /)

Οι πίνακες powerOf2, a αποκτούν τις ίδιες τιμές· ο δεύτερος τρόπος, του a, είναι πιο συνοπτικός, κατανοητός και επεκτάσιμος (πώς θα γράφαμε τις 100 πρώτες δυνάμεις;). Θα τον συναντήσουμε ξανά παρακάτω και θα τον περιγράψουμε εκεί.

''3.1.4.1 Σχετικές ενσωματωμένες συναρτήσεις''

Πολύ συχνά, ειδικά σε συναρτήσεις και υπορουτίνες που έχουν ως όρισμα ένα πίνακα, είναι απαραίτητο να γνωρίζουμε το πλήθος των στοιχείων του ή το πλήθος θέσεων μιας διάστασής του. Η Fortran παρέχει τη συνάρτηση SIZE( ) που μας δίνει αυτές τις πληροφορίες. Έτσι, έχοντας τον ακόλουθο πίνακα

REAL, DIMENSION(10,10,30) :: a

το πλήθος των στοιχείων του είναι SIZE(a) , η πρώτη διάσταση έχει SIZE(a,1) θέσεις, η δεύτερη SIZE(a,2) και η τρίτη SIZE(a,3) θέσεις.

Σπανιότερα χρειάζονται τα όρια των θέσεων σε κάθε διάσταση. Το κάτω όριο της πρώτης διάστασης στη δήλωση του πίνακα a είναι το LBOUND(a,1) , της δεύτερης LBOUND(a,2) , κλπ. Τα άνω όρια είναι αντίστοιχα UBOUND(a,1) , UBOUND(a,2) , κλπ.

Περισσότερες τέτοιες συναρτήσεις θα δούμε στο §4.2.

''3.1.5 Πίνακας άγνωστου πλήθους στοιχείων''

Τονίσαμε στις δηλώσεις ότι το πλήθος των στοιχείων του πίνακα πρέπει να είναι αριθμητική σταθερά, γνωστή εκ των προτέρων. Η Fortran μας δίνει τη δυνατότητα να ορίσουμε πίνακες με αρχικά άγνωστο πλήθος στοιχείων, που θα το μάθουμε, βέβαια, κατά την εκτέλεση του προγράμματος. Π.χ. αν διαβάζουμε ένα πίνακα από το πληκτρολόγιο ή από αρχείο, πιθανό είναι να μη γνωρίζουμε κατά τη μεταγλώττιση πόσα στοιχεία θα έχει. Αυτήν την πληροφορία θα την πάρουμε από το χρήστη ή από το αρχείο πριν αρχίσει η εισαγωγή των στοιχείων, οπότε θα πρέπει να κατασκευάσουμε τον πίνακα.

Η δήλωση

REAL, ALLOCATABLE :: a(Smiling

δηλώνει ένα πραγματικό μονοδιάστατο πίνακα με όνομα a. Στοιχεία δεν έχει ακόμα. Αν θέλουμε να αποκτήσει N στοιχεία, όπου το N είναι ακέραια ποσότητα, σταθερή ή μεταβλητή, πρέπει να δεσμεύσουμε μνήμη δίνοντας την εντολή

ALLOCATE(a(N))

Όταν δε θα χρειαζόμαστε πλέον τον συγκεκριμένο πίνακα καλό είναι να να “ελευθερώσουμε” τη μνήμη που κατείχε δίνοντας την εντολή

DEALLOCATE(a)

Σε αντιστοιχία με τις δηλώσεις σταθερού πλήθους στοιχείων, ο πίνακας με πρώτη θέση την a(M) και τελευταία την a(N) όπου M,N όχι απαραίτητα σταθερές ποσότητες, δημιουργείται με την εντολή

ALLOCATE(a(M:N))

Διδιάστατος (και, γενικεύοντας, πολυδιάστατος) πίνακας που θα δημιουργηθεί κατά την εκτέλεση δηλώνεται ως εξής

REAL, ALLOCATABLE :: b(:,Smiling

Η δέσμευση μνήμης γίνεται με την εντολή

ALLOCATE(b(M,N))

όπου M,N ποσότητες τύπου INTEGER . Η αποδέσμευση μπορεί να γίνει με την εντολή

DEALLOCATE(b)

Οι εντολές ALLOCATE/DEALLOCATE μπορούν να χρησιμοποιηθούν για πολλούς πίνακες ταυτόχρονα:

PROGRAM alloc
IMPLICIT NONE
REAL, DIMENSION (Smiling, ALLOCATABLE :: a, b
REAL, DIMENSION (:,Smiling, ALLOCATABLE :: c
INTEGER :: M,N, K, P

READ (*,*) M, N, K, P

ALLOCATE (a(M), b(N), c(K,P))

! give values to a, b, c
! use a, b, c

DEALLOCATE(a)
DEALLOCATE(b, c)
END PROGRAM alloc

Στη Fortran 95 (σε αντίθεση με τη Fortran 90), η μνήμη ενός πίνακα που δεσμεύθηκε με την εντολή ALLOCATE ελευθερώνεται αυτόματα μόλις η ροή της εκτέλεσης συναντήσει το τέλος του προγράμματος ή υποπρογράμματος στο οποίο δηλώθηκε ο πίνακας. Επομένως, δεν είναι απαραίτητη η κλήση της DEALLOCATE , καλό είναι όμως να γίνεται.

===== 3.2 Εντολές Επανάληψης (Βρόχοι) =====

Βασική ανάγκη ύπαρξης ενός υπολογιστή είναι να μας απαλλάξει από απλές επαναληπτικές διαδικασίες, εκτελώντας τις με ακρίβεια και ταχύτητα. Η Fortran παρέχει δύο εντολές, τις DO και FORALL , για τον προγραμματισμό επαναληπτικών διεργασιών. Η FORALL εισήχθη για να υποστηρίξει τον χειρισμό πινάκων σε υπολογιστές με πολλούς επεξεργαστές, φυσικούς ή λογικούς, και πληροφορεί το μεταγλωττιστή ότι μπορεί να κάνει κατανομή των πράξεων μεταξύ πινάκων στους διαθέσιμους επεξεργαστές. Αυτοί τις εκτελούν ταυτόχρονα και επομένως ο συνολικός χρόνος εκτέλεσης του προγράμματος μικραίνει. Σε συστήματα με ένα επεξεργαστή δεν υπάρχει ουσιαστική διαφορά στη μεταγλώττιση από την DO . Δεν θα αναφερθούμε περισσότερο στην εντολή FORALL .

Η εντολή DO υλοποιεί στη Fortran τον επαναληπτικό βρόχο, ένα βασικό χαρακτηριστικό κάθε γλώσσας προγραμματισμού. Παρουσιάζεται με δύο παραλλαγές:
Για συγκεκριμένο, γνωστό, αριθμό επαναλήψεων, και
για άγνωστο εκ των προτέρων, πλήθος επαναλήψεων.

''3.2.1 DO για συγκεκριμένο πλήθος επαναλήψεων''

Για να καταλάβουμε την ανάγκη ύπαρξης του συγκεκριμένου DO , ας δούμε το εξής πρόβλημα: Έχουμε την ακόλουθη δήλωση πίνακα

REAL :: a(5)

Η απόδοση τιμών στον πίνακα από το πληκτρολόγιο, σύμφωνα με όσα ξέρουμε μέχρι τώρα, μπορεί να γίνει ως εξής

READ (*,*) a(1), a(2), a(3), a(4), a(5)

ενώ το άθροισμα των στοιχείων του είναι

REAL :: sum_a

sum_a = a(1) + a(2) + a(3) + a(4) + a(5)

Τα παραπάνω είναι εφικτά καθώς το πλήθος των στοιχείων είναι πολύ μικρό. Μπορούμε να τροποποιήσουμε τις παραπάνω εντολές ώστε να τις φέρουμε σε κατάλληλη μορφή για επανάληψη. Έτσι, η ανάγνωση μπορεί να γίνει ως εξής

READ (*,*) a(1)
READ (*,*) a(2)
READ (*,*) a(3)
READ (*,*) a(4)
READ (*,*) a(5)

ή, ισοδύναμα,

INTEGER :: i

i=1
READ (*,*) a(i)

i=2
READ (*,*) a(i)

i=3
READ (*,*) a(i)

i=4
READ (*,*) a(i)

i=5
READ (*,*) a(i)

Αντίστοιχα, ο υπολογισμός του αθροίσματος μπορεί να γραφεί

INTEGER :: i
REAL :: sum_a

sum_a = 0.0

i=1
sum_a = sum_a + a(i)

i=2
sum_a = sum_a + a(i)

i=3
sum_a = sum_a + a(i)

i=4
sum_a = sum_a + a(i)

i=5
sum_a = sum_a + a(i)

Παρατηρήστε ότι φέραμε και τα δύο τμήματα κώδικα στη μορφή που μια εντολή επαναλαμβάνεται αυτούσια ενώ υπάρχει μια ακέραια μεταβλητή που αυξάνεται διαδοχικά κατά 1 . Η χρήση της εντολής DO απλοποιεί τους κώδικες ως εξής

INTEGER :: i

DO i = 1,5
READ (*,*) a(i)
END DO

και

INTEGER :: i
REAL :: sum_a

sum_a = 0.0

DO i = 1,5
sum_a = sum_a + a(i)
END DO

''3.2.1.1 Σύνταξη''

Η γενικευμένη σύνταξη της DO είναι η ακόλουθη
DO μεταβλητή = αρχική τιμή, τελική τιμή, βήμα αύξησης
....
....
END DO

Η “μεταβλητή” είναι υποχρεωτικά ακέραια και αναφέρεται ως μεταβλητή ελέγχου. Οι “αρχική τιμή” , “τελική τιμή” , “βήμα αύξησης” είναι σταθεροί ακέραιοι αριθμοί ή εκφράσεις που δίνουν ακέραια τιμή. Το “βήμα αύξησης” αν παραλείπεται (μαζί με το (,) που προηγείται) θεωρείται ότι είναι 1. Αν δίνεται, επιτρέπεται να είναι θετικό ή αρνητικό αλλά όχι 0.

Η εκτέλεση γίνεται σε βήματα ως εξής:
η “μεταβλητή” αποκτά αρχικά την “αρχική τιμή” .
γίνεται ο ακόλουθος έλεγχος:
Αν το “βήμα αύξησης” είναι θετικό, ελέγχεται αν η “μεταβλητή” είναι μικρότερη ή ίση από την “τελική τιμή” .
Αν το “βήμα αύξησης” είναι αρνητικό, ελέγχεται αν η “μεταβλητή” είναι μεγαλύτερη ή ίση από την “τελική τιμή” .
Ισοδύναμα, μπορούμε να πούμε ότι ελέγχεται αν με διαδοχικές προσθέσεις του “βήματος αύξησης” στη “μεταβλητή” μπορούμε να φτάσουμε την “τελική τιμή” .
Αν ο έλεγχος στο βήμα 2 είναι θετικός, εκτελούνται οι εντολές (καμμία, μία ή περισσότερες) που περικλείονται μεταξύ των DO , END DO .
Αν ο έλεγχος στο βήμα 2 είναι αρνητικός, η ροή του προγράμματος συνεχίζει με την πρώτη εντολή μετά το END DO , διακόπτοντας την επανάληψη.
Προστίθεται στη “μεταβλητή” το “βήμα αύξησης” και επαναλαμβάνεται το βήμα 2 (έλεγχος της συνθήκης).

Συνέπεια της παραπάνω διαδικασίας είναι ότι η “μεταβλητή” (η οποία φυσικά πρέπει να έχει δηλωθεί) θα έχει την “αρχική τιμή” αν δεν εκτελεστεί καθόλου η επανάληψη, την τιμή “τελική τιμή” + “βήμα αύξησης” αν εκτελεστεί πλήρως ενώ θα έχει μια ενδιάμεση τιμή αν η επανάληψη διακοπεί πριν την κανονική λήξη της.

Υπάρχει η δυνατότητα να τροποποιηθούν οι ποσότητες “αρχική τιμή” , “τελική τιμή” , “βήμα αύξησης” κατά τη διάρκεια της επανάληψης, όμως οι νέες τους τιμές δεν παίζουν κανένα ρόλο στο πλήθος των επαναλήψεων· αυτό καθορίζεται από τις αρχικές τιμές τους.

''3.2.1.2 Παρατήρηση''

Βασιζόμενοι στην παραπάνω ανάλυση, αν έχουμε εντολές που επαναλαμβάνονται, φροντίζουμε να τις φέρουμε στη μορφή
“ακέραια μεταβλητή” = “ακέραια μεταβλητή” + “βήμα αύξησης”
σύνολο εντολών (εξαρτώμενες ή μη από την “ακέραια μεταβλητή” , αλλά με την ίδια μορφή ανεξάρτητα από την τιμή της μεταβλητής).
Αν το επιτύχουμε αυτό, τότε μπορούμε να γράψουμε την παραπάνω σειρά επαναλαμβανόμενων εντολών πιο συνοπτικά και κατανοητά με τη χρήση του DO ... END DO .

3.2.1.2.1 Παραδείγματα:

Το άθροισμα των πρώτων 100 ακεραίων αριθμών, από το 1 ως το 100, υπολογίζεται με τον ακόλουθο κώδικα:

INTEGER :: sum, i

sum = 0
DO i = 1, 100
sum = sum + i
END DO

Το άθροισμα των άρτιων ακεραίων αριθμών στο διάστημα [0, 1000] υπολογίζεται με τον ακόλουθο κώδικα:

INTEGER :: sum, i

sum = 0
DO i = 0, 1000, 2
sum = sum + i
END DO

Η εκτύπωση των αριθμών 99, 97, 95,..., 3, 1, με αυτή τη σειρά, μπορεί να γίνει με τον κώδικα

INTEGER :: i

DO i = 99, 1, -2
PRINT *, i
END DO

Η εκτύπωση των αριθμών 0.0 , 0.1 , 0.2 , ...999.9 , 1000.0 γίνεται ως εξής

INTEGER :: i

DO i = 0, 10000
PRINT *, 0.1 * i
END DO

Η ανάγνωση από το πληκτρολόγιο των στοιχείων του πίνακα

REAL:: a(10)

γίνεται με τον ακόλουθο κώδικα

INTEGER :: k

DO k = 1, SIZE(a)
READ *, a(k)
END DO

''3.2.1.3 Παρατήρηση''

Τα δύο τελευταία παραδείγματα μπορούν να γραφούν πιο συνοπτικά με τη χρήση του υπονοούμενου DO:

INTEGER :: i

PRINT *, (0.1 * i, i=0, 10000)

και

INTEGER :: k

READ *, (a(k), k=1, SIZE(a))

Με τη χρήση της συγκεκριμένης μορφής του DO ο κώδικας για την εκτύπωση κάθε δεύτερου στοιχείου του πίνακα a γίνεται ως εξής

INTEGER :: i

PRINT *, (a(i), i=1,SIZE(a),2)

ή, πιο γενικά,

INTEGER :: i

PRINT *, (a(i), i=LBOUND(a,1),UBOUND(a,1),2)

Όπως παρατηρούμε από τα παραδείγματα, το υπονοούμενο DO περιλαμβάνεται σε παρενθέσεις και αποτελείται από μία μόνο έκφραση ή εντολή, ακολουθούμενη από (γενικά) τριάδα των τιμών για τον καθορισμό μια ακέραιας μεταβλητής.

''3.2.2 DO για απροσδιόριστο αριθμό επαναλήψεων''

Υπάρχει συχνά η ανάγκη να επαναλάβουμε ένα τμήμα εντολών χωρίς να γνωρίζουμε εκ των προτέρων το πλήθος των επαναλήψεων. Π.χ. κατά την είσοδο δεδομένων από τον χρήστη, οι τιμές που εισάγονται μπορεί να απαιτείται να ικανοποιούν κάποια συνθήκη. Σε τέτοια περίπτωση χρειάζεται να επαναλάβουμε τις εντολές ανάγνωσης και ελέγχου των δεδομένων, έως ότου τα δεδομένα είναι κατάλληλα. Δεν γνωρίζουμε όμως πόσες φορές θα χρειαστεί για να δώσει ο χρήστης αποδεκτές τιμές. Τέτοιες καταστάσεις αντιμετωπίζονται από τη Fortran 95 με την εντολή DO χωρίς μεταβλητή ελέγχου. Η σύνταξή της είναι απλή:

DO
....
....
END DO

Με τη συγκεκριμένη μορφή του DO οι εντολές που περικλείονται στα DO ... END DO εκτελούνται για πάντα. Προφανώς, πρέπει με κάποιον τρόπο να διακόπτουμε την επανάληψη όταν επιθυμούμε. Η Fortran παρέχει την εντολή EXIT (§3.2.3) που κάνει ακριβώς αυτό.

''3.2.3 EXIT''

Η εντολή EXIT μπορεί να εμφανιστεί μόνο μέσα σε DO ... END DO με ή χωρίς μεταβλητή ελέγχου. Όταν εκτελεστεί, προκαλεί την έξοδο από το βρόχο που την περικλείει. Η επόμενη εντολή που εκτελείται είναι αυτή που ακολουθεί το END DO .

Παράδειγμα χρήσης: αν επιθυμούμε να διαβάσουμε έναν πραγματικό αριθμό και να προχωρήσουμε στην εκτέλεση μόνο αν είναι θετικός τότε μπορούμε να γράψουμε

REAL :: x

DO
READ *, x
IF (x > 0.0) EXIT
END DO

''3.2.4 CYCLE''

Η εντολή CYCLE μπορεί να εμφανιστεί μόνο μέσα σε DO ... END DO . Όταν εκτελεστεί, προκαλεί τη μεταφορά της ροής του προγράμματος αμέσως μετά την τελευταία εντολή στο σώμα του DO που την περικλείει· παραλείπει, επομένως, τις εντολές που την ακολουθούν. Το αμέσως επόμενο βήμα της διαδικασίας επανάληψης που εκτελείται είναι η μεταβολή της ακέραιας μεταβλητής ελέγχου του DO κατά το βήμα αύξησης. Στην περίπτωση του DO χωρίς μεταβλητή ελέγχου, §3.2.2, ουσιαστικά ξαναρχίζει την επανάληψη.

Παράδειγμα χρήσης είναι το ακόλουθο:

INTEGER :: i
REAL :: x

DO i=1,100

READ *, x
IF (x < 0.0) CYCLE ! reject x

PRINT *, "Square root is ", SQRT(x)
END DO

''3.2.5 Σημείωση''

Έχουμε τη δυνατότητα να αποδώσουμε ένα όνομα σε κάθε βρόχο DO . Αυτό γίνεται δίνοντας το όνομα (ακολουθούμενο από ':') πριν το DO , στην ίδια γραμμή, και συμπληρώνωντας υποχρεωτικά το αντίστοιχο END DO , όπως στο παρακάτω παράδειγμα:

onoma: DO i=1, 10
....
....
END DO onoma

To όνομα σχηματίζεται με τους κανόνες που είδαμε για τις μεταβλητές, §1.2.2.

Η ονοματοδοσία στο DO είναι χρήσιμη στις εντολές EXIT και CYCLE . Αν τις χρησιμοποιήσουμε με τον τρόπο που περιγράψαμε στις αντίστοιχες παραγράφους, εκτελούν τη λειτουργία τους για το βρόχο που τις περικλείει. Αν, όμως, προσδιορίσουμε και το όνομα ενός βρόχου, δηλαδή,

CYCLE onoma

ή

EXIT onoma

τότε οι εντολές εκτελούνται για το βρόχο με το όνομα onoma. Έτσι, π.χ., έξοδος από φωλιασμένο βρόχο γίνεται ως εξής

outer: DO i=1,10
DO j=1,10
DO k=1,20
...
...
IF (k > i+j) EXIT outer
END DO
END DO
END DO outer

Στο παραπάνω παράδειγμα, η εντολή που θα εκτελεστεί μετά το EXIT (όταν αυτό εκτελεστεί) θα είναι η πρώτη εντολή μετά το END DO outer .

4.

Ένα βασικό χαρακτηριστικό που εισήχθη με τη Fortran 90 για τους ενσωματωμένους τελεστές και συναρτήσεις και επεκτάθηκε με τη Fortran 95 ώστε να ισχύει και για τελεστές και συναρτήσεις που ορίζει ο χρήστης, είναι η υποστήριξη για τις πράξεις κατά στοιχείο (elemental).

===== 4.1 Πράξεις κατά στοιχείο =====

Είδαμε ότι οι τελεστές = , + , - ,*,/,** και οι ενσωματωμένες συναρτήσεις του Πίνακα 2.1 εκτελούν πράξεις σε απλές ποσότητες (μεταβλητές ή σταθερές). Στη Fortran 95 μπορούν να εκτελέσουν τις ίδιες πράξεις και σε πίνακες σαν να εκτελούσαμε τη συγκεκριμένη πράξη σε κάθε ένα στοιχείο χωριστά. Για παράδειγμα, η ανάθεση τιμής σε μία απλή μεταβλητή γίνεται ως εξής

REAL :: x

x = 3.4

Η ανάθεση της ίδιας τιμής σε όλα τα στοιχεία ενός πίνακα μπορεί να γίνει με τον ακόλουθο κώδικα

REAL :: a(10)
INTEGER :: i

DO i=1,SIZE(a)
a(i) = 3.4
END DO

Εναλλακτικά, μπορούμε να χρησιμοποιήσουμε την εντολή ανάθεσης

REAL :: a(10)

a = 3.4

Ο τελεστής ( = ) σε αυτήν την περίπτωση αναθέτει τη σταθερή τιμή σε κάθε ένα στοιχείο του a. Αυτή τη διαδικασία ο μεταγλωττιστής έχει τη δυνατότητα να τη βελτιστοποιήσει ή να την εκτελέσει παράλληλα, σε πολλούς επεξεργαστές ταυτόχρονα, καθώς δεν του καθορίζουμε τη σειρά των αναθέσεων όπως κάνουμε στον κώδικα με το DO .

Αντίστοιχα ισχύουν και όταν έχουμε ανάθεση πίνακα σε πίνακα, §3.1.2. Η εντολή a = b , όπου a, b πίνακες ίδιας διάστασης και πλήθους στοιχείων, κάνει αντιγραφή κάθε στοιχείου του b στο αντίστοιχο του a.

Άλλο παράδειγμα είναι η πρόσθεση πινάκων ίδιας διάστασης και πλήθους στοιχείων και γενικά οποιαδήποτε πράξη με πίνακες. Έτσι το άθροισμα δύο πινάκων μπορεί να γίνει είτε με το

REAL, DIMENSION (10) :: a, b, c
INTEGER :: i
...... ! apodosi timwn se a,b

DO i=1,SIZE(a)
c(i) = a(i) + b(i)
END DO

είτε πιο συνοπτικά με τον κώδικα

REAL, DIMENSION (10) :: a, b, c
...... ! apodosi timwn se a,b

c = a + b

Οι γνωστοί τελεστές από τις πράξεις μεταξύ απλών ποσοτήτων, όταν δρουν μεταξύ πινάκων εκτελούν τις αντίστοιχες πράξεις στα ίδιας θέσης στοιχεία σαν να είχαμε κατάλληλο DO . Προσέξτε ότι ο τελεστής * εκτελεί πολλαπλασιασμό κατά στοιχεία και όχι τον πολλαπλασιασμό πινάκων, δηλαδή οι παρακάτω κώδικες

REAL, DIMENSION (10) :: a, b, c
...... ! apodosi timwn se a,b

c = a * b

και

REAL, DIMENSION (10) :: a, b, c
INTEGER :: i
...... ! apodosi timwn se a,b

DO i=1,SIZE(a)
c(i) = a(i) * b(i)
END DO

είναι ισοδύναμοι.

Απλή μεταβλητή, όταν εμφανίζεται σε πράξη με πίνακα, “μετατρέπεται” σε πίνακα κατάλληλης διάστασης και πλήθους στοιχείων ώστε ή πράξη να μπορεί να γίνει. Αυτό σημαίνει ότι με τις ακόλουθες δηλώσεις

REAL :: lambda, a(10), b(20)

οι κώδικες

a = lambda * a

και

b = lambda * b

είναι σωστοί: πολλαπλασιάζονται με lambda τα στοιχεία των a,b, όσα κι αν είναι αυτά. Αντίθετα, ο κώδικας

a = a * b

δεν έχει νόημα.

Εκτός από τους τελεστές, και οι ενσωματωμένες συναρτήσεις δρουν σε καθένα στοιχείο του ορίσματός τους αν αυτό είναι πίνακας και επιστρέφουν πίνακα αντίστοιχης διάστασης και πλήθους στοιχείων. Π.χ. ο κώδικας

REAL :: x, y
..... ! apodosi timis sto x

y = SQRT(x)

υπολογίζει την τετραγωνική ρίζα του x και την αναθέτει στο y. Η ίδια ακριβώς εντολή υπολογίζει τη ρίζα κάθε στοιχείου του πίνακα a στον παρακάτω κώδικα και την αναθέτει στα αντίστοιχα στοιχεία του όμοιου πίνακα b:

REAL :: a(20), b(20)
..... ! apodosi timis sto a

b = SQRT(a)

Ισοδύναμα, θα μπορούσαμε να γράψουμε

REAL :: a(20), b(20)
INTEGER :: i
..... ! apodosi timis sto a

DO i=1,SIZE(a)
b(i) = SQRT(a(i))
END DO

''4.1.1 Τμήμα πίνακα''

Ένα σύνολο στοιχείων ενός πίνακα αποτελεί για τη Fortran έναν νέο πίνακα. Π.χ. αν a είναι μονοδιάστατος πίνακας με 10 στοιχεία, τα στοιχεία 3,4,5,6,7 αυτού συμβολίζονται με a(3:7) αποτελούν ένα άλλο μονοδιάστατο πίνακα, με 5 στοιχεία· η συνάρτηση SIZE(a(3:7)) επιστρέφει την τιμή 5 . Ο νέος αυτός πίνακας μπορεί να συμμετέχει σε οποιαδήποτε πράξη χρειάζεται μονοδιάστατο πίνακα 5 στοιχείων. Έτσι, ο παρακάτω κώδικας

REAL :: a(10), b(20)

a = b(1:10)

είναι σωστός (τα 10 πρώτα στοιχεία του b ανατίθενται στα αντίστοιχα του a), ενώ αντίθετα η ανάθεση a=b είναι λάθος. Φυσικά,
a ≡ a(1:SIZE(a))
για κάθε πίνακα a (που έχει δηλωθεί ότι η αρίθμηση των στοιχείων του αρχίζει από το 1).

Επίσης, έχουμε τη δυνατότητα να ορίσουμε νέο πίνακα από ένα σύνολο στοιχείων κάποιου άλλου, χωρίς αυτά να είναι συνεχόμενα. Έτσι, το πρώτο, τρίτο, πέμπτο και έβδομο στοιχείο ενός πίνακα a αποτελούν πίνακα μονοδιάστατο με 4 στοιχεία. Ο νέος πίνακας είναι ο a(1:7:2) . Ο τρίτος αριθμός στην παρένθεση καθορίζει το βήμα της αύξησης, το οποίο μπορεί να είναι και αρνητικό. Γενικά, από έναν πίνακα a δημιουργούμε άλλον προσδιορίζοντας αρχική θέση, τελική θέση και βήμα αύξησης ως εξής
a(αρχική θέση : τελική θέση : βήμα μεταβολής)
Αν το βήμα μεταβολής παραλείπεται, υπονοείται το 1. Αν παραλείπεται η αρχική ή η τελική θέση υπονοείται το πρώτο και το τελευταίο στοιχείο αντίστοιχα. Αν το βήμα είναι θετικό, πρέπει η αρχική θέση να μην είναι μετά την τελική· το αντίστροφο πρέπει να ισχύει αν το βήμα είναι αρνητικό. Έτσι
a(:10) equiv; $
a(4:) equiv; $
a(:) equiv; $

Ενδιαφέρουσα εφαρμογή των παραπάνω είναι η ακόλουθη: έστω ότι θέλουμε να αντιστρέψουμε τα στοιχεία ενός πίνακα. Μπορούμε να το κάνουμε με τον ακόλουθο κώδικα

REAL :: a(100), temp
INTEGER :: i

DO i = 1, SIZE(a)/2
temp = a(SIZE(a) - i + 1)
a(SIZE(a) - i + 1) = a(i)
a(i) = temp
END DO

Προσέξτε ότι πρέπει να χρησιμοποιήσουμε μια προσωρινή μεταβλητή καθώς δεν μπορούμε να κάνουμε απευθείας εναλλαγή των a(i) , a(SIZE(a)-i+1) . Ισοδύναμα και πολύ πιο απλά, μπορούμε να δώσουμε την ακόλουθη εντολή

a = a(SIZE(a):1:-1)

Ο νέος πίνακας στο δεξί μέλος της ανάθεσης έχει τα ίδια στοιχεία με τον a αλλά με ανάποδη αρίθμηση· προσέξτε ότι πρώτα υπολογίζεται και μετά ανατίθεται στον a.

Ανάλογα ισχύουν και για πολυδιάστατους πίνακες. Έτσι αν ο a είναι πίνακας 10 x 20 , με δήλωση

INTEGER :: a(10,20)

τότε ο a(1:5,1:6) είναι διδιάστατος πίνακας 5 x 6 και αποτελείται από τα στοιχεία των 5 πρώτων γραμμών και 6 πρώτων στηλών.

Παρατηρήστε ότι μπορούμε να επιλέξουμε μία γραμμή (π.χ. την τρίτη) ή στήλη (π.χ. την πέμπτη) του διδιάστατου πίνακα a με τους συμβολισμούς a(3,Smiling και a(:,5) αντίστοιχα. Οι νέοι αυτοί πίνακες είναι μονοδιάστατοι.

===== 4.2 Συναρτήσεις με όρισμα πίνακα =====

Η Fortran 95 παρέχει ένα πλήθος συναρτήσεων που δρουν σε πίνακες και εκτελούν πολλές συνήθεις πράξεις. Είδαμε μέχρι τώρα (§3.1.4) τις SIZE( ) , LBOUND( ) , UBOUND( ) . Άλλες είναι οι:

SUM( ) : Η συνάρτηση SUM( ) με όρισμα πίνακα μας επιστρέφει το άθροισμα όλων των στοιχείων του (αρκεί να είναι ακέραιου, πραγματικού ή μιγαδικού τύπου). Π.χ.

INTEGER :: a(10), s

a = (/ 1, 5, 4, 2, 12, 9, 8, 2, -4, -7 /)

s = SUM(a) ! s = 32

PRODUCT( ) : Η συνάρτηση PRODUCT( ) με όρισμα πίνακα μας επιστρέφει το γινόμενο όλων των στοιχείων του (αρκεί να είναι ακέραιου, πραγματικού ή μιγαδικού τύπου). Π.χ.

INTEGER :: a(10), p

a = (/ 1, 5, 4, 2, 12, 9, 8, 2, -4, -7 /)

p = PRODUCT(a) ! p = 1935360

DOT_PRODUCT( ): Η συνάρτηση DOT_PRODUCT( ) δέχεται για ορίσματα δύο μονοδιάστατους πίνακες με ίδιο πλήθος στοιχείων αλλά όχι απαραίτητα ίδιο τύπο. Η DOT_PRODUCT(a,b) επιστρέφει
2#2aibi αν ο πίνακας a είναι ακέραιος ή πραγματικός.
2#2ai * bi αν ο πίνακας a είναι τύπου COMPLEX .
Αν οι πίνακες a,b είναι τύπου LOGICAL , το DOT_PRODUCT(a,b) επιστρέφει .TRUE. αν τουλάχιστο σε ένα ζεύγος αντίστοιχων στοιχείων των πινάκων είναι και τα δύο μέλη .TRUE. , αλλιώς επιστρέφει .FALSE.
MATMUL( ): Η συνάρτηση MATMUL( ) παίρνει δύο πίνακες ως ορίσματα και υπολογίζει το γινόμενό τους. Τουλάχιστον ένας πίνακας πρέπει να είναι διδιάστατος· ο άλλος μπορεί να είναι μονοδιάστατος. Οι διαστάσεις τους πρέπει να είναι συμβατές, σύμφωνα με όσα προβλέπονται από το μαθηματικό ορισμό του γινομένου πινάκων. Το αποτέλεσμα είναι μονοδιάστατος ή διδιάστατος πίνακας με τις διαστάσεις που προκύπτουν από τη μαθηματική πράξη. Επομένως, η μαθηματική πράξη C = A . B με A, B, C πίνακες γράφεται ως C=MATMUL(A,B) .

Όπως γνωρίζουμε από τα μαθηματικά, το γινόμενο των πινάκων A, B είναι πίνακας C με στοιχεία

Cij = 3#3aikbkj .

Η χρήση της MATMUL( ) με ορίσματα πραγματικούς διδιάστατους πίνακες ισοδυναμεί με τον ακόλουθο κώδικα

INTEGER :: i, j, k
REAL :: s

DO i = 1,SIZE(A,1)
DO j = 1,SIZE(B,2)
s = 0.0
DO k = 1, SIZE(A,2)
s = s + a(i,k) * b(k,j)
END DO
c(i,j) = s
END DO
END DO

TRANSPOSE( ): Η συνάρτηση TRANSPOSE( ) επιστρέφει τον ανάστροφο πίνακα του ορίσματός της. Έτσι αν

REAL :: a(10, 20), at(20,10)

at = TRANSPOSE(a)

Ο πίνακας at έχει στη θέση (i, j) την τιμή του στοιχείου (j, i) του πίνακα a.
COUNT( ): Η συνάρτηση COUNT( ) με όρισμα λογική συνθήκη στην οποία συμμετέχει πίνακας, υπολογίζει το πλήθος των στοιχείων του πίνακα που ικανοποιούν τη συγκεκριμένη συνθήκη. Π.χ. το COUNT(A > 10) μετρά το πλήθος των στοιχείων του (ακέραιου) πίνακα a που είναι μεγαλύτερα από 10 .
MAXVAL( )/MINVAL( ): Οι συναρτήσεις MAXVAL( ) και MINVAL( ) επιστρέφουν τη μέγιστη και ελάχιστη, αντίστοιχα, τιμή που είναι αποθηκευμένη στον πίνακα που δέχονται ως όρισμα.
MAXLOC( )/MINLOC( ): Οι συναρτήσεις MAXLOC( ) και MINLOC( ) δέχονται πίνακα ως όρισμα και επιστρέφουν σε μονοδιάστατο πίνακα ακεραίων τους δείκτες που προσδιορίζουν τη θέση του μεγαλύτερου/μικρότερου στοιχείου. Π.χ. αν ο a είναι διδιάστατος πίνακας που έχει στη θέση (2, 3) το ελάχιστο στοιχείο του, η κλήση της συνάρτησης MINLOC(a) επιστρέφει σε μονοδιάστατο πίνακα δύο στοιχείων τις τιμές 2 (πρώτη θέση), 3 (δεύτερη θέση). Φυσικά, ο πίνακας αυτός πρέπει να δηλωθεί κατάλληλα από το χρήστη.
ALL( ): Η συνάρτηση ALL( ) δέχεται ως όρισμα μια λογική συνθήκη, απλή ή σύνθετη, στην οποία συμμετέχει πίνακας. Εάν για κάθε στοιχείο του πίνακα η συνθήκη είναι αληθής, η συνάρτηση επιστρέφει .TRUE. . Σε άλλη περίπτωση επιστρέφει .FALSE. .
ANY( ): Η συνάρτηση ANY( ) δέχεται ως όρισμα μια λογική συνθήκη, απλή ή σύνθετη, στην οποία συμμετέχει πίνακας. Εάν έστω και για ένα στοιχείο του πίνακα η συνθήκη είναι αληθής, η συνάρτηση επιστρέφει .TRUE. . Στην αντίθετη περίπτωση επιστρέφει .FALSE. .
Δε θα αναφερθούμε σε επιπλέον ορίσματα που δέχονται οι παραπάνω συναρτήσεις.

===== 4.3 WHERE =====

Η εντολή WHERE μπορεί να θεωρηθεί ως ένα γενικευμένο IF , στη συνθήκη του οποίου συμμετέχει πίνακας, και στο σώμα του έχουμε μόνο αναθέσεις σε πίνακες. Πιο συγκεκριμένα, η σύνταξη του WHERE είναι

WHERE (condition)
....
....
ELSEWHERE
....
....
END WHERE

Η συνθήκη condition, είναι λογική έκφραση, απλή ή σύνθετη, που περιλαμβάνει πίνακα. Το σώμα του WHERE επιτρέπεται να έχει μόνο εντολές ανάθεσης σε ένα ή περισσότερους πίνακες ίδιας διάστασης και πλήθους στοιχείων με τον πίνακα της συνθήκης. Για τις θέσεις του συγκεκριμένου πίνακα στις οποίες τα στοιχεία ικανοποιούν τη συνθήκη, εκτελούνται οι αντίστοιχες αναθέσεις στο τμήμα πριν το ELSEWHERE . Για όσα στοιχεία δεν ικανοποιείται η συνθήκη, εκτελούνται οι αντίστοιχες αναθέσεις στο τμήμα μετά το ELSEWHERE . Π.χ., έστω ότι θέλουμε να αντιγράψουμε τα θετικά στοιχεία του πραγματικού πίνακα a στον όμοιο πίνακα b, ενώ θέλουμε να μηδενίσουμε τα στοιχεία του b αν το αντίστοιχο στοιχείο του a δεν είναι θετικό. Αυτό γίνεται είτε με τον κώδικα

DO i = 1,SIZE(a)
IF (a(i) > 0.0) THEN
b(i) = a(i)
ELSE
b(i) = 0.0
END IF
END DO

είτε με το

WHERE (a > 0.0)
b = a
ELSEWHERE
b = 0.0
END WHERE

Στην περίπτωση που δεν έχουμε αναθέσεις όταν η συνθήκη είναι ψευδής μπορούμε να παραλείψουμε το ELSEWHERE . Εάν, επιπλέον, έχουμε μια εντολή ανάθεσης για αληθή συνθήκη, μπορούμε να τη γράψουμε στην ίδια γραμμή με το WHERE . Έτσι, μπορούμε να πάρουμε την τετραγωνική ρίζα του a στον b με τον ακόλουθο κώδικα

WHERE (a>=0.0) b = SQRT(a)

Τα στοιχεία του b που αντιστοιχούν σε αρνητικές τιμές του a δεν τροποποιούνται.

5.

===== 5.1 Παραγόμενοι Τύποι =====

Όπως αναφέραμε ήδη, οι σχετιζόμενες ποσότητες του προβλήματός μας, με ίδιο τύπο, είναι προτιμότερο να αναπαρίστανται στον κώδικά μας με πίνακα παρά με ισάριθμες ανεξάρτητες μεταβλητές. Στην οργάνωση του κώδικα, αλλά και στη διαχείριση των μεταβλητών, οι πίνακες παρουσιάζουν σημαντικά πλεονεκτήματα. Παρ' όλα αυτά, δεν είναι η καταλληλότερη δομή για την αναπαράσταση ορισμένων σύνθετων εννοιών.

Καταρχήν, για σχετιζόμενες ποσότητες που δεν είναι ίδιου τύπου δεν μπορούμε να εκφράσουμε τη μεταξύ τους σύνδεση με πίνακες. Π.χ. ας υποθέσουμε ότι επιθυμούμε να περιγράψουμε σε πρόγραμμα για υπολογιστή, τη δανειστική λειτουργία μιας πανεπιστημιακής Βιβλιοθήκης· σε πρώτη φάση, θέλουμε να καταγράψουμε τα βιβλία που έχουν δανειστεί οι φοιτητές, να γνωρίζουμε σε ποια υπάρχει καθυστέρηση στην επιστροφή, κλπ. Έχουμε, επομένως, ένα σύνολο φοιτητών (που χαρακτηρίζονται από το όνομά τους, το Τμήμα στο οποίο ανήκουν, τον Αριθμό Μητρώου στη Σχολή τους, το έτος εισαγωγής τους, κλπ.), και ένα πλήθος βιβλίων (που χαρακτηρίζονται από τον τίτλο τους, το συγγραφέα, τον εκδοτικό οίκο, το έτος έκδοσης, κλπ.), που μπορούν να δανειστούν. Η απλοϊκή υλοποίηση σε κώδικα περιλαμβάνει δηλώσεις πινάκων για την ομαδοποίηση του καθενός από τα χαρακτηριστικά που αναφέρθηκαν. Έτσι, για 500 φοιτητές και 3000 βιβλία θα είχαμε

INTEGER, PARAMETER :: N = 500 ! number of students
INTEGER, PARAMETER :: M = 3000 ! number of books

CHARACTER(30) :: studentName(N)
CHARACTER(50) :: bookAuthor(M)
CHARACTER(60) :: bookTitle(M)
INTEGER :: studentAM(N), studentYear(N), bookYear(M)

Παρατηρήστε ότι τίποτε στους παραπάνω ορισμούς δεν εκφράζει την σχέση που έχουν (ή δεν έχουν) τα στοιχεία των πινάκων μεταξύ τους· τίποτε, εκτός πιθανόν από το όνομά τους, δεν υποδηλώνει ότι κάποια περιγράφουν χαρακτηριστικά της έννοιας “φοιτητής” και κάποια άλλα της έννοιας “βιβλίο” . Προσέξτε ότι ένα σωστά επιλεγμένο όνομα μεταβλητής διευκολύνει αρκετά τον προγραμματιστή ή τον αναγνώστη στην κατανόηση του κώδικα, αλλά για τον compiler δεν έχει κάποιο ιδιαίτερο νόημα. Όχι μόνο δε γίνεται η ομαδοποίηση των χαρακτηριστικών στο επίπεδο των δύο βασικών εννοιών του προβλήματός μας (όνομα φοιτητή, αριθμός μητρώου, κλπ. μαζί και, ξεχωριστά, αλλά πάλι συγκεντρωμένα, ο τίτλος του βιβλίου, το όνομα του συγγραφέα, κλπ.) αλλά, χειρότερα, συνδέονται όλα τα ονόματα μαζί, όλοι οι αριθμοί μητρώου, κλπ. Η συγκεκριμένη οργάνωση των δεδομένων μας, και συνεπώς, και του υπόλοιπου κώδικά μας, είναι αρκετά διαφορετική από αυτή που υπαγορεύει το πρόβλημα που προσομοιώνουμε.

Μια καλύτερη, πιο “φυσική” προσέγγιση είναι να ορίσουμε νέους τύπους ποσοτήτων που θα μπορούν να περιγράψουν συνολικά τα χαρακτηριστικά κάθε έννοιας. Είναι προφανές ότι οι ενσωματωμένοι τύποι δεν ανταποκρίνονται σε αυτήν την απαίτηση: η έννοια “φοιτητής” δεν είναι ακέραιος ούτε πραγματικός, για μεταβλητή τέτοιου τύπου δεν ορίζεται η πρόσθεση ή εξαγωγή τετραγωνικής ρίζας!

Η Fortran 95 μας δίνει τη δυνατότητα να ορίσουμε νέους, σύνθετους τύπους. Αυτό επιτυγχάνεται με τη δομή TYPE η σύνταξή της οποίας είναι
TYPE onoma
Δήλωση μεταβλητής
Δήλωση μεταβλητής
Δήλωση μεταβλητής
......
END TYPE onoma
Η TYPE μπορεί να εμφανιστεί μόνο στο τμήμα των δηλώσεων του προγράμματός μας και περιλαμβάνει μεταξύ των TYPE ... END TYPE μόνο δηλώσεις μεταβλητών. Ο τύπος των μεταβλητών μπορεί να είναι από τους ενσωματωμένους ή από αυτούς που όρισε ο προγραμματιστής σε προηγούμενο ορισμό TYPE . Αν το επιθυμούμε, μπορούμε να έχουμε στις δηλώσεις και αποδόσεις αρχικών τιμών.

Δήλωση ποσότητας αυτού του τύπου γίνεται ως εξής:

TYPE (onoma) :: x

Προσέξτε ότι κατά τον ορισμό του νέου τύπου το όνομά του δεν περικλείεται σε παρενθέσεις. Αντίθετα, στη δήλωση ποσότητας το όνομά του είναι εντός παρενθέσεων.

Βασιζόμενοι στα παραπάνω, μπορούμε να ορίσουμε νέους τύπους που περιλαμβάνουν ως μέλη κατάλληλους θεμελιώδεις τύπους, και αποτελούν ένα πρώτο βήμα βελτίωσης του προγράμματος, καθώς ακολουθούν τον τρόπο οργάνωσης των δεδομένων του προβλήματός μας:

TYPE student
CHARACTER(30) :: studentName
INTEGER :: studentAM, studentYear
END TYPE student

TYPE book
CHARACTER(50) :: bookAuthor
CHARACTER(60) :: bookTitle
INTEGER :: bookYear
END TYPE book

Οι μεταβλητές που βρίσκονται σε διαφορετικούς παραγόμενους τύπους μπορούν να έχουν το ίδιο όνομα. Επομένως, μπορούμε να συντομεύσουμε τα ονόματά τους ως εξής

TYPE student
CHARACTER(30) :: name
INTEGER :: am, year
END TYPE student

TYPE book
CHARACTER(50) :: author
CHARACTER(60) :: title
INTEGER :: year
END TYPE book

χωρίς να προκαλείται “σύγκρουση” ονομάτων.

Δήλωση ποσοτήτων αυτών των νέων τύπων γίνεται με τον ακόλουθο κώδικα

TYPE (student) :: s
TYPE (book) :: b

INTEGER, PARAMETER :: N = 500 ! number of students
INTEGER, PARAMETER :: M = 3000 ! number of books

TYPE (student) :: students(N)
TYPE (book) :: books(M)

Με τον κώδικα αυτό ορίσαμε δύο σύνθετες μεταβλητές, τις s και b, και δύο πίνακες από student και book .

Πρόσβαση στα μέλη μια ποσότητας σύνθετου, παραγόμενου τύπου έχουμε με τη χρήση του τελεστή %. Έτσι, ανάθεση τιμής σε μια σύνθετη ποσότητα μπορεί να γίνει ως εξής

TYPE (book) :: b

b%title = "Fortran 95/2003 explained"
b%author = "Michael Metcalf, John Reid, Malcolm Cohen"
b%year = 2004

Τα μέλη μιας σύνθετης ποσότητας συμπεριφέρονται όπως ακριβώς αν είχαν δηλωθεί ανεξάρτητα, απλώς το όνομά τους περιλαμβάνει το όνομα της ποσότητας στην οποία ανήκουν. Έτσι π.χ., η εκτύπωση γίνεται

PRINT *, b%title
PRINT *, b%author

ενώ η συμμετοχή σε μία συνθήκη είναι

IF (b%year > 2003) THEN
.....
END IF

Ανάθεση τιμής σε μια σύνθετη ποσότητα γίνεται σε κάθε μέλος χωριστά, όπως είδαμε. Εναλλακτικά, μπορεί να της δοθεί τιμή ως σύνολο, με ανάθεση άλλης ποσότητας του ίδιου τύπου

TYPE (book) :: b, c

b%title = "Fortran 95/2003 explained"
b%author = "Michael Metcalf, John Reid, Malcolm Cohen"
b%year = 2004

c = b

ή με ανάθεση σταθερής ποσότητας αυτού του τύπου που δημιουργείται με σταθερά μέλη ως εξής:

TYPE (book) :: b

b = book("Michael Metcalf, John Reid, Malcolm Cohen", &
"Fortran 95/2003 explained", &
2004)

Στο δεξί μέλος της εντολής ανάθεσης εμφανίζεται το όνομα του τύπου ακολουθούμενου από σταθερές ποσότητες εντός παρενθέσεων, χωριζόμενων με κόμμα. Αυτές αντιστοιχούν σε κάθε μέλος του τύπου με τη σειρά που αυτό εμφανίζεται στον ορισμό.

Οι δύο παραπάνω τρόποι για την απόδοση τιμής ως σύνολο, μπορούν να εμφανίζονται και σε δήλωση με ταυτόχρονη απόδοση τιμής.

Επιπλέον, όπως και για πίνακες, μπορεί να δοθεί τιμή με ανάγνωση από το πληκτρολόγιο ή από αρχείο, ποσοτήτων κατάλληλης σειράς, πλήθους και τύπου όταν εκτελεστεί η εντολή READ για ολόκληρη τη δομή:

TYPE (book) :: b

READ *, b

Εκτός από την περίπτωση που έχουμε διαφορετικού τύπου ποσότητες που συνδέονται και, όπως αναλύσαμε, δεν μπορούμε να χρησιμοποιήσουμε πίνακα για να εκφράσουμε τη συσχέτισή τους, υπάρχει περίπτωση να έχουμε σχετιζόμενες ποσότητες του ίδιου τύπου.

Έστω, π.χ., ότι έχουμε ένα σύνολο 70 ημερομηνιών (ημέρα, μήνας, έτος) με τις αντίστοιχες θερμοκρασίες ενός τόπου. Η απλοϊκή περιγραφή του σε κώδικα θα ήταν

INTEGER, PARAMETER :: N = 70
INTEGER, DIMENSION (N) :: day, month, year
REAL :: temperature(N)

Όπως παρατηρούμε, οι τέσσερις πίνακες είναι για το μεταγλωττιστή ισοδύναμοι παρόλο που σαν έννοιες δεν είναι. Μια βελτίωση (αμφιβόλου αξίας!) θα ήταν η ακόλουθη

INTEGER, PARAMETER :: N = 70
INTEGER, DIMENSION (3,N) :: date
REAL :: temperature(N)

Με την παραπάνω δήλωση του date θα πρέπει να έχουμε πάντα στο μυαλό μας ότι στη θέση date(1,j) είναι η ημέρα της j μέτρησης, στη θέση date(2,j) είναι ο μήνας και στη θέση date(3,j) είναι το έτος. (Αρκεί να μη δώσουμε τον κώδικα σε Αμερικανό προγραμματιστή για να το συμπληρώσει, καθώς θα δυσκολευτεί από την “αντίστροφη” σειρά ημέρας και μήνα!). Με αυτή τη διόρθωση έχουμε διαφοροποιήσει τη ημερομηνία (date) από τον πίνακα των θερμοκρασιών (temperature), όμως, επιπλέον, έχουμε εισαγάγει “αφύσικη” σύνδεση μεταξύ διαφορετικών ημερομηνιών: τα στοιχεία date(1,5), date(2,5), date(3,5), date(1,Cool για το πρόγραμμά μας είναι ισοδύναμα παρόλο που τα τρία πρώτα αναφέρονται στην ίδια ημερομηνία ενώ το τέταρτο σε άλλη.

Ουσιαστική βελτίωση μπορούμε να έχουμε με το ορισμό νέου τύπου, της ημερομηνίας (date):

TYPE date
INTEGER :: day, month, year
END TYPE date

και νέου τύπου, αυτού της μέτρησης (measurement):

TYPE measurement
TYPE (date) :: d
REAL :: temperature
END TYPE measurement

Με τα παραπάνω, η κατάλληλη δήλωση για την οργάνωση των δεδομένων του προγράμματός μας είναι η ακόλουθη

TYPE (measurement) :: meas(70)

Η απόδοση τιμών στο j στοιχείο αυτού του πίνακα γίνεται ως εξής

meas(j)%date%day = 21
meas(j)%date%month = 3
meas(j)%date%year = 2006
meas(j)%temperature = 24.5

===== 5.2 Είσοδος/Έξοδος Δεδομένων =====

Μέχρι τώρα, χρησιμοποιούσαμε τις εντολές READ/WRITE (ή PRINT ) για την είσοδο και έξοδο δεδομένων, αφήνοντας τον μεταγλωττιστή να αποφασίσει πώς θα κάνει τις αντίστοιχες διαδικασίες βασιζόμενος στον τύπο των ποσοτήτων που εμφανίζονται στις εντολές. Αυτό είναι συχνά αρκετό· κάποιες φορές όμως, επιθυμούμε να έχουμε μεγαλύτερο έλεγχο σε σημεία όπως στο πόσα δεκαδικά ψηφία ενός πραγματικού θα τυπώνονται, αν θα αλλάζει γραμμή μετά την εκτύπωση, κλπ. Για αυτό το σκοπό πρέπει να προσδιορίζουμε λεπτομερώς στις εντολές εισόδου/εξόδου τι και πώς θα μεταφερθεί. Η πληροφορία αυτή δίνεται είτε απ' ευθείας στις εντολές εισόδου/εξόδου με μια σειρά χαρακτήρων, §2.1.1, είτε έμμεσα δια της δήλωσης FORMAT . Το περιεχόμενο της σειράς χαρακτήρων ή του FORMAT θα το περιγράψουμε παρακάτω.

Ο απευθείας προσδιορισμός του FORMAT ως σειράς χαρακτήρων γίνεται ως εξής

READ (*, "(......)") a, b, i
WRITE (*, "(......)") f, g, hhhh

ή, ισοδύναμα,

READ "(......)", a, b, i
PRINT "(.......)", f, g, hhhh

Επομένως, το δεύτερο * στα READ(*,*)/WRITE(*,*) ή το μόνο * στα READ */PRINT * που άφηνε ελεύθερο το μεταγλωττιστή να αποφασίσει τις λεπτομέρειες της μεταφοράς των δεδομένων έχει αντικατασταθεί από τη σειρά χαρακτήρων. Σημειώστε ότι ο πρώτος και ο τελευταίος χαρακτήρας της είναι παρενθέσεις.

Εναλλακτικά, μπορούμε να προσδιορίσουμε τη διαμόρφωση σε ξεχωριστή δήλωση με την εντολή FORMAT . Η συγκεκριμένη εντολή αριθμείται με αριθμό ενός έως πέντε ψηφίων πριν τη λέξη FORMAT και αυτός ο αριθμός μπαίνει αντί για το * στις εντολές εισόδου/εξόδου. Η εντολή FORMAT μπορεί να εμφανίζεται σε οποιοδήποτε σημείο του προγράμματος (πριν το END ή το CONTAINS ) και συνήθως τοποθετείται είτε αμέσως μετά την εντολή μεταφοράς δεδομένων είτε στο τέλος του προγράμματος. Μια εντολή FORMAT μπορεί να χρησιμοποιηθεί σε καμία, μία ή και περισσότερες εντολές READ/WRITE , μέσω του αριθμού της, ο οποίος είναι της επιλογής του προγραμματιστή αλλά μοναδικός σε όλο το πρόγραμμα (ή συνάρτηση, υπορουτίνα, MODULE ). Οι εντολές μεταφοράς δεδομένων στον παραπάνω κώδικα μπορούν να γραφούν ισοδύναμα

READ (*, 12) a, b, i
12 FORMAT (......)

WRITE (*, 25) f, g, hhhh
25 FORMAT (.......)

και

READ 12, a, b, i
12 FORMAT (......)

PRINT 25, f, g, hhhh
25 FORMAT (.......)

Παρατηρείστε ότι μετά τη λέξη FORMAT ακολουθεί το περιεχόμενο της σειράς χαρακτήρων χωρίς τα εισαγωγικά (που μπορεί να είναι απλά ή διπλά).

''5.2.1 Περιεχόμενο του FORMAT''

Στα παρακάτω θα περιγράψουμε τι περιλαμβάνεται εντός των παρενθέσεων σε εντολή FORMAT ή εντός εισαγωγικών και παρενθέσεων στον άμεσο προσδιορισμό στο READ/WRITE .

Για κάθε ποσότητα που θα εκτυπωθεί ή θα διαβαστεί, πρέπει να προσδιορίσουμε στο FORMAT τον τύπο της και το μέγιστο πλήθος των ψηφίων της, πόσες θέσεις, δηλαδή, θα χρειαστούν για να γίνει η μεταφορά.

Επομένως, για
Ακέραιη ποσότητα
χρησιμοποιούμε το χαρακτήρα `I' ακολουθούμενο από το εύρος του πεδίου w, δηλαδή το πλήθος των θέσεων. Η γενική μορφή είναι Iw. Με την εντολή

WRITE (*, "(I5)") 12

τυπώνεται το
12
Η εκτύπωση γίνεται σε όσες θέσεις προσδιορίζει το εύρος και είναι στοιχισμένη στα δεξιά. Αν το εύρος είναι μεγαλύτερο από το πλήθος των ψηφίων, συμπεριλαμβανόμενου και του προσήμου, ο αριθμός συμπληρώνεται με κενά. Αν είναι μικρότερο, ο ακέραιος δεν τυπώνεται αλλά εμφανίζονται στο πεδίο w φορές ο χαρακτήρας *. Μπορούμε να προσδιορίσουμε ότι το εύρος είναι 0, δηλαδή να δώσουμε στο format I0 · αυτό υποδηλώνει ότι ο μεταγλωττιστής θα επιλέξει το ελάχιστο εύρος του πεδίου ώστε ο αριθμός να εκτυπωθεί κανονικά.

Κατά την ανάγνωση ενός ακεραίου, πρέπει να λαμβάνουμε υπόψη στον προσδιορισμό του εύρους και τα κενά που προηγούνται. Έτσι, η είσοδος
-345
δοθεί ως είσοδος σε εντολή ανάγνωσης, αυτή πρέπει να έχει format με εύρος τουλάχιστον 7, ώστε να διαβαστεί:

READ (*, "(I7)") i

Κατά την εκτύπωση μπορούμε να προσδιορίσουμε και το ελάχιστο πλήθος ψηφίων που θέλουμε εκτυπωθεί, n, με το format Iw.n. Αν ο αριθμός δεν έχει τουλάχιστον n ψηφία, συμπληρώνεται με 0 . Η εντολή

WRITE (*, "(I5.3)") 99

τυπώνει το
099
Πραγματική ποσότητα
χρησιμοποιούμε έναν από τους χαρακτήρες `E' ή `EN' ή `ES' ή `F' ακολουθούμενου από το εύρος του πεδίου, w, και το πλήθος των ψηφίων μετά την τελεία, d, στη μορφή Fw.d. Το w υποδηλώνει το συνολικό εύρος πεδίου, μετρώντας και το τυχόν πρόσημο και την τελεία. Στην ανάγνωση δεδομένων το d αγνοείται. Οι χαρακτήρες στο FORMAT είναι ισοδύναμοι κατά την ανάγνωση αλλά προκαλούν διαφορετική εκτύπωση.

Ο χαρακτήρας `F' τυπώνει τον αριθμό σε δεκαδική μορφή. Έτσι, το

WRITE(*,"(F9.2)") -146.29

τυπώνει
-146.29
Για διευκόλυνση, μπορούμε να προσδιορίσουμε ότι το εύρος είναι 0, δηλαδή να δώσουμε στο format για εύρος w το 0. Έτσι υποδηλώνουμε ότι ο μεταγλωττιστής θα πρέπει να επιλέξει το ελάχιστο εύρος του πεδίου ώστε ο αριθμός να εκτυπωθεί κανονικά.

Οι χαρακτήρες `E', `EN', `ES' στο FORMAT προκαλούν την εκτύπωση του πραγματικού αριθμού στην μορφή που περιέχει το E ακολουθούμενου από τη δύναμη του 10. Πιο συγκεκριμένα, ο χαρακτήρας `E' τυπώνει στη μορφή 4#40.xxxxE4#4yy ή 4#40.xxxxE4#4yyy ή ακόμα και 4#40.xxxx4#4yyy (αν ο εκθέτης είναι μεγαλύτερος από 99 ). Η εντολή

WRITE (*, "(E10.2)") 123E5

τυπώνει
0.12E+08
Οι χαρακτήρες `ES' εκτυπώνουν παρόμοια με το E αλλά ο αριθμός πριν το E είναι κατ' απόλυτη τιμή μεταξύ του 1 και του 10 . Η εντολή

WRITE (*, "(ES10.2)") 123E5

τυπώνει
1.23E+07
Οι χαρακτήρες `EN' εκτυπώνουν παρόμοια με το E αλλά ο εκθέτης είναι πολλαπλάσιο του 3 και ο αριθμός πριν το E είναι κατ' απόλυτη τιμή μεταξύ του 1 και του 1000 . Η εντολή

WRITE (*, "(EN10.2)") 123E5

τυπώνει
12.30E+06

Εάν στην είσοδο του προγράμματος δοθεί ο αριθμός
9.3729
η ανάγνωσή του μπορεί να γίνει με το ακόλουθο READ :

READ (*, "(F8.2)") x ! or E8.2 or EN8.2 or ES8.2

Αν η είσοδος είναι
93.729E-1
το READ που χρειάζεται πρέπει να έχει εύρος τουλάχιστον 10 και είναι

READ (*, "(F10.1)") x ! or E10.1 or EN10.1 or ES10.1

Όπως και στους ακεραίους, το εύρος πεδίου στην εκτύπωση με τον χαρακτήρα `F' μόνο, μπορεί να δοθεί 0 ώστε ο μεταγλωττιστής να επιλέξει το κατάλληλο για τη σωστή μεταφορά του αριθμού.
Μιγαδική ποσότητα
χρησιμοποιούμε δύο FORMAT για πραγματικούς, χωριζόμενα με κόμμα. Π.χ. η εντολή

WRITE (*,"(F6.1,E8.1)") (0.1, 100.0)

τυπώνει
0.1 0.1E+03
Μεταξύ των δύο FORMAT (που δεν είναι απαραιτήτως ίδια) μπορούμε να έχουμε σειρά χαρακτήρων που θα τυπωθεί, χωριζόμενη με κόμματα από τα FORMAT και, βέβαια, εντός διαφορετικών εισαγωγικών από το συνολικό FORMAT . Π.χ. το

WRITE (*,"(F6.1,', ',E8.1)") (0.1, 100.0)

τυπώνει
0.1, 0.1E+03
Λογική ποσότητα
χρησιμοποιούμε τον χαρακτήρα L ακολουθούμενου από το εύρος πεδίου, w. Η εκτύπωση των .TRUE. και .FALSE. μεταφέρει τους χαρακτήρες `T' και `F' αντίστοιχα. Οι συγκεκριμένοι χαρακτήρες αρκούν στην είσοδο για να αποδοθούν οι αντίστοιχες τιμές.
Ποσότητα Χαρακτήρα ή Σειρά Χαρακτήρων
χρησιμοποιούμε τον χαρακτήρα `A', προαιρετικά ακολουθούμενου από το εύρος πεδίου. Αν αυτό δεν προσδιορίζεται ρητά, η εκτύπωση ή η ανάγνωση γίνεται με όσο εύρος χρειάζεται για να υπάρξει σωστή και πλήρης μεταφορά. Π.χ. η εντολή

WRITE (*,"(A)") "This is a statement"

τυπώνει
This is a statement
Για οποιαδήποτε ενσωματωμένη ποσότητα μπορεί να χρησιμοποιηθεί ο χαρακτήρας `G' ακολουθούμενος από το εύρος του πεδίου, w, και το πλήθος των ψηφίων μετά την τελεία, d, στη μορφή Gw.d. Για ποσότητες ακέραιες, λογικές ή χαρακτήρα ισοδυναμεί με Iw, Lw, Aw ενώ για πραγματικό ισοδυναμεί με το Ew.d εκτός αν ο αριθμός είναι κατάλληλα μικρός και μπορεί να εκτυπωθεί με κάποιο αντίστοιχο `F'.

Ποσότητα με τύπο που δημιουργήθηκε από τον προγραμματιστή μπορεί να διαβαστεί ή να εκτυπωθεί συνολικά, αρκεί να παραθέσουμε, χωριζόμενα με κόμματα, τα κατάλληλα FORMAT για τα μέλη της, με τη σειρά που εμφανίζονται οι δηλώσεις στο TYPE .

Αν η εκτύπωση ή ανάγνωση περιλαμβάνει πολλές ποσότητες, παραθέτουμε τα αντίστοιχα FORMAT χωριζόμενα με κόμμα. Συνεχόμενα ίδια FORMAT προσδιορισμού μπορούν να γραφούν με συντομία γράφοντας μόνο ένα αφού προσδιορίσουμε το πλήθος τους, όπως στο παράδειγμα: το

100 FORMAT (I4,I4,I4)

ισοδυναμεί με

100 FORMAT (3I4)

ενώ το

200 FORMAT (I4,F4.2,I4,F4.2)

μπορεί να γραφεί

200 FORMAT (2(I4,F4.2))

''5.2.2 FORMAT ελέγχου''

Εκτός από τους χαρακτήρες που είδαμε για κάθε τύπο ποσότητας, σε ένα FORMAT μπορούμε να συμπεριλάβουμε (μεταξύ άλλων)
σειρές χαρακτήρων, οι οποίες τυπώνονται αυτούσιες,
τον χαρακτήρα `/', που προκαλεί αλλαγή γραμμής,
τους χαρακτήρες `nX', όπου n ακέραιος αριθμός. Το FORMAT αυτό προκαλεί την εκτύπωση n κενών.
Κατά την εκτύπωση καλό είναι ο πρώτος τουλάχιστον χαρακτήρας να είναι κενός, είτε ρητά, προσδιορίζοντας το 1X, είτε λόγω μεγαλύτερου εύρους από το απολύτως απαραίτητο για την εκτύπωση της ποσότητας.

Επίσης, αν επιθυμούμε να εμφανίζεται το πρόσημο (+) κατά την εκτύπωση θετικών αριθμών, πρέπει στο FORMAT να περιλάβουμε τους χαρακτήρες `sp', πριν το FORMAT των αριθμών. Η εκτύπωση θετικών αριθμών χωρίς πρόσημο επαναφέρεται με τους χαρακτήρες `ss' ή όταν τελειώσει το FORMAT . Έτσι, οι εντολές

WRITE (*,"(F8.2,sp,F8.2)") 5.12, 2.3
WRITE (*,"(F8.2)") 9.16
WRITE (*,"(F8.2,sp,F8.2,F5.1,ss,F5.1)") 5.12, 2.3, 4.1, 3.5

τυπώνουν
5.12 +2.30
9.16
5.12 +2.30 +4.1 3.5

''5.2.3 Αλλαγή γραμμής''

Στην περίπτωση που δεν επιθυμούμε να αλλάξει γραμμή μετά την εκτύπωση, συμπληρώνουμε την εντολή WRITE με το advance="no" . Προσέξτε ότι μπορούμε να το κάνουμε μόνο αν προσδιορίζεται και FORMAT για την εκτύπωση (και όχι απλώς *). Π.χ.

WRITE (*, "(A)", advance="no") "Give number:"
READ (*,*) x

===== 5.3 Αρχεία =====

Μέχρι τώρα, η είσοδος και η έξοδος δεδομένων γινόταν από το πληκτρολόγιο και την οθόνη αντίστοιχα. Μπορούμε, όμως, να χρησιμοποιήσουμε και εξωτερικά ή εσωτερικά αρχεία.

Παρακάτω θα αναφερθούμε μόνο στα αρχεία σειριακής προσπέλασης.

''5.3.1 Εξωτερικά Αρχεία''

Τα εξωτερικά αρχεία είναι αυτά που μας παρέχει το λειτουργικό σύστημα του υπολογιστή και αποθηκεύονται συνήθως στο σκληρό δίσκο. Για να μεταφέρουμε δεδομένα από και προς ένα τέτοιο αρχείο πρέπει να συνδεθούμε σε αυτό. Η εντολή OPEN κάνει αυτό ακριβώς. Η σύνταξή της είναι

OPEN(unit = u, file = fname, status = stat, position= pos, &
action = act)

Το unit= είναι το μόνο υποχρεωτικό όρισμα. Σε αυτό δίνουμε έναν ακέραιο μη αρνητικό αριθμό u της επιλογής μας, μοναδικό για κάθε αρχείο, ο οποίος το προσδιορίζει μονοσήμαντα. Η μέγιστη τιμή αυτού του αριθμού καθορίζεται από τον μεταγλωττιστή, συνήθως όμως είναι 99 . Όπως θα δούμε, το file= είναι επίσης απαραίτητο σε όλες σχεδόν τις περιπτώσεις. Σε αυτό δίνουμε το όνομα του αρχείου ως σειρά χαρακτήρων, σταθερή ή μεταβλητή. Έτσι, αν επιθυμούμε να ανοίξουμε το αρχείο με όνομα "input.dat" και να του δώσουμε τον αριθμό 12 , πρέπει να εκτελέσουμε την εντολή

OPEN(unit = 12, file = "input.dat")

ή λίγο πιο απλά

OPEN(12, file = "input.dat")

χωρίς να είναι απαραίτητο να προσδιορίσουμε τίποτε επιπλέον.

Τα υπόλοιπα ορίσματα, αν υπάρχουν, μπορούν να πάρουν συγκεκριμένες τιμές. Αυτές είναι:
για το status= ένα από τα "old", "new", "replace", "scratch", "unknown". Προσδιορίζουν αντίστοιχα ότι το αρχείο
είναι παλαιό (υπάρχει),
είναι καινούργιο (δεν υπάρχει και θα δημιουργηθεί),
είναι είτε παλαιό και θα αντικατασταθεί είτε καινούργιο και θα δημιουργηθεί,
είναι για προσωρινή χρήση (και δεν επιτρέπεται να δίνεται όνομα με το file= ),
είναι άγνωστης κατάστασης.
Αν δεν προσδιορίσουμε κάποιο από αυτά, θεωρείται ότι δώσαμε το status="unknown" .
για το position= ένα από τα
"rewind", που τοποθετεί το δείκτη για είσοδο ή έξοδο δεδομένων στην αρχή του αρχείου,
"append", που τοποθετεί το δείκτη στο τέλος του, ή
"asis" που θέτει το δείκτη στην αρχή αν το αρχείο είναι νέο, ή εκεί που βρίσκεται αν έχει ήδη συνδεθεί με άλλο OPEN . Αυτή είναι η προκαθορισμένη τιμή αν παραληφθεί το position= .
για το action= οι τιμές είναι "read", "write" ή "readwrite" αν θα χρησιμοποιηθεί μόνο για είσοδο δεδομένων, ή μόνο για έξοδο ή και για τα δυο, αντίστοιχα.
Π.χ. η σύνδεση, στον αριθμό 34, του αρχείου με όνομα "data" που υπάρχει ήδη και θα χρησιμοποιηθεί για είσοδο δεδομένων γίνεται με την εντολή

OPEN (unit = 34, file ="data", status = "old", action = "read")

Αν επιθυμούμε να εκτελέσουμε ένα READ ή WRITE στο συγκεκριμένο αρχείο, βάζουμε τον αριθμό του σαν πρώτο όρισμα στην παρένθεση της εντολής αντί για το *, δηλαδή

READ(34, "(F8.2)") x

ή, ισοδύναμα,

READ(unit = 34, fmt = "(F8.2)") x

Αφού ολοκληρωθεί η χρήση του αρχείου πρέπει να αποσυνδεθούμε. Αυτό επιτυγχάνεται με την εντολή

CLOSE(u)

όπου u ο αριθμός του συγκεκριμένου αρχείου. Κατόπιν, ο αριθμός αυτός είναι ελεύθερος να ξαναχρησιμοποιηθεί για άλλο αρχείο.

5.3.1.0.1 Παρατηρήσεις
Υπάρχουν συγκεκριμένοι αριθμοί αρχείων που υποδηλώνουν είσοδο από το πληκτρολόγιο και έξοδο στην οθόνη. Αυτοί συνήθως είναι το 5 και το 6 αντίστοιχα. Το 0 αντιστοιχεί στο αρχείο που διοχετεύονται τα μηνύματα λάθους του προγράμματος (δηλαδή την οθόνη).
Μπορούμε να έχουμε συνδεδεμένα ταυτόχρονα πολλά αρχεία.

''5.3.2 Εσωτερικά Αρχεία''

Τα εσωτερικά αρχεία είναι σειρές χαρακτήρων που δημιουργούνται από το πρόγραμμά μας με τους γνωστούς τρόπους και έχουν διάρκεια ζωής το χρόνο εκτέλεσης του προγράμματος. Μπορούμε να επιλέξουμε το μέγεθός τους ώστε να επαρκούν για ο,τιδήποτε θελήσουμε να εγγράψουμε σε αυτά. Δε χρειάζονται σύνδεση και αποσύνδεση, η δήλωσή τους είναι η μόνη αναγκαία. Χρησιμοποιούνται απ' ευθείας σε εντολές READ/WRITE , με το όνομά τους στη θέση του πρώτου *. Παράδειγμα χρήσης, και ταυτόχρονα μια σημαντική εφαρμογή των εσωτερικών αρχείων, είναι το ακόλουθο

CHARACTER(50) :: str
INTEGER :: i
i = 3
WRITE(str, "(A,I0,A)") "fname_", i, ".dat"
! str is "fname_3.dat"

Η εγγραφή των τριών δεδομένων με το προσδιοριζόμενο FORMAT γίνεται στη σειρά χαρακτήρων str. Αυτή κατόπιν μπορεί να χρησιμοποιηθεί για τον προσδιορισμό του ονόματος αρχείου σε επόμενο OPEN .

6. Συναρτήσεις-Υπορουτίνες

===== 6.1 Εισαγωγή =====

Στα προηγούμενα κεφάλαια έχουν παρουσιαστεί κάποιες από τις βασικές εντολές-δομές της Fortran, αρκετές ώστε να μπορούμε να γράψουμε σχετικά πολύπλοκους κώδικες. Η συγκέντρωση, όμως, όλου του κώδικα στο κυρίως πρόγραμμα, ειδικά όταν είναι μεγάλος, καθιστά δύσκολη την κατανόησή του και, κυρίως, τη διόρθωση λαθών. Σχεδόν πάντα ο κώδικας αποτελείται από τμήματα που, σε μεγάλο βαθμό, είναι ανεξάρτητα μεταξύ τους. Αυτά μπορούν να απομονωθούν σε αυτόνομες συναρτήσεις ή υπορουτίνες, να αποτελούν, δηλαδή, ομάδες εντολών με συγκεκριμένο όνομα, οι οποίες θα καλούνται όπου και όσες φορές χρειάζεται από το κυρίως πρόγραμμα ( PROGRAM ... END PROGRAM ) ή άλλα υποπρογράμματα, χρησιμοποιώντας μόνο αυτό το όνομα. Αυτές οι ομάδες εντολών θα παραμετροποιούνται συνήθως από μία ή περισσότερες ποσότητες, τα ορίσματα της συνάρτησης ή υπορουτίνας. Καθώς η συνάρτηση δεν έχει ουσιαστική διαφορά με την υπορουτίνα, θα αναφερόμαστε σε αυτές συνολικά ως υποπρογράμματα.

Η οργάνωση του προγράμματός μας σε υποπρογράμματα είναι ένα πρώτο βήμα στην απλοποίηση του κώδικα και μας επιτρέπει να επικεντρωνόμαστε σε συγκεκριμένες, κατά το δυνατόν απλές, εργασίες κατά την ανάπτυξη ή διόρθωση του προγράμματος. Έτσι π.χ., ένας αλγόριθμος μπορεί να υλοποιηθεί, να διορθωθεί και να βελτιστοποιηθεί αυτόνομα, ανεξάρτητα από τον υπόλοιπο κώδικα και, επομένως, να μπορεί να χρησιμοποιείται από εμάς ή άλλους σε διαφορετικά προγράμματα. Από τη στιγμή που θα υπάρξει απομόνωση του κώδικα σε αυτόνομη συνάρτηση, η χρήση του απλοποιείται σημαντικά καθώς μας απασχολεί μόνο το πώς τον καλούμε και τι ορίσματα πρέπει να “περάσουμε” στο υποπρόγραμμα και όχι το ποιούς ακριβώς υπολογισμούς εκτελεί.

Η οργάνωση του κώδικα σε δεδομένα και σε διαδικασίες που επιδρούν σε αυτά περιγράφεται ως δομημένος (structured) ή διαδικαστικός (procedural) προγραμματισμός.

Ένα πλήρες πρόγραμμα FORTRAN πρέπει να περιλαμβάνει ένα, και μόνο ένα, κυρίως πρόγραμμα, δηλαδή κώδικα μεταξύ PROGRAM ... END PROGRAM . Οι εντολές στον κώδικα είναι αυτές που περιγράψαμε στα προηγούμενα κεφάλαια; επιπλέον, μπορούμε να έχουμε κλήσεις συναρτήσεων ή υπορουτινών που έχουν οριστεί πριν ή μετά το κυρίως πρόγραμμα και έχουν δηλωθεί σε αυτό κατάλληλα. Για ειδικές περιπτώσεις μπορούμε να δηλώσουμε υποπρογράμματα μέσα σε άλλο υποπρόγραμμα.

===== 6.2 Η έννοια του υποπρογράμματος =====

Ας προσπαθήσουμε να κατανοήσουμε την έννοια της συνάρτησης στη Fortran με βάση τη μαθηματική συνάρτηση. Στα μαθηματικά μπορούμε να ορίσουμε ότι

f (x) = x2 +5x - 2 .

Αυτό σημαίνει ότι οι συγκεκριμένες πράξεις, x2 + 5x - 2 , έχουν αποκτήσει ένα όνομα, f , μέσω του οποίου θα τις χρησιμοποιούμε όποτε χρειαζόμαστε το αποτέλεσμά τους. Παρατηρούμε ότι εξαρτώνται από ένα σύμβολο, το x , και επομένως, δεν μπορούν να εκτελεστούν και να μας δώσουν αποτέλεσμα. Ο συμβολισμός f (x) υποδηλώνει ότι η συνάρτηση f έχει ως παράμετρο, ως όρισμα όπως λέμε, την ποσότητα x . Για τις συναρτήσεις πραγματικής μεταβλητής το x συμβολίζει έναν πραγματικό αριθμό. Όταν επιθυμούμε να εκτελέσουμε τις πράξεις x2 + 5x - 2 για κάποια τιμή του x μπορούμε να χρησιμοποιήσουμε (να καλέσουμε) τη συνάρτηση f με ταυτόχρονο προσδιορισμό της τιμής του ορίσματος, του συμβόλου x . Γι' αυτό γράφουμε, π.χ., y = f (2.5) αντί για το ισοδύναμο αλλά πιο εκτεταμένο y = 2.52 +5 . 2.5 - 2 . Οι πράξεις μπορούν να εκτελεστούν αφού δώσουμε τιμή στο σύμβολο x , θέσουμε, δηλαδή, τη δεδομένη τιμή όπου εμφανίζεται το x . Το αποτέλεσμά τους για τη συγκεκριμένη τιμή επιστρέφεται (αποδίδεται) στο όνομα της συνάρτησης και μπορούμε να το κρατήσουμε σε κάποια κατάλληλη ποσότητα. Μια μαθηματική συνάρτηση μπορεί να έχει περισσότερα από ένα ορίσματα (παραμέτρους). Αφού τα προσδιορίσουμε όλα μπορούν να εκτελεστούν οι πράξεις τις οποίες αντιπροσωπεύει. Προφανώς, μια συνάρτηση μπορεί να κληθεί όσες φορές επιθυμούμε.

Στη Fortran, κατά πλήρη αντιστοιχία: ένα τμήμα κώδικα μπορούμε να το ξεχωρίσουμε από το κύριο σώμα των εντολών του προγράμματός μας και να του δώσουμε ένα όνομα. Αυτό το τμήμα κώδικα μπορεί να εξαρτάται από καμμία, μία ή περισσότερες ποσότητες. Στον ορισμό του υποπρογράμματος τα ορίσματα δεν είναι τίποτε άλλο παρά σύμβολα που αντιστοιχούν σε ποσότητες συγκεκριμένων τύπων. Όταν το καλέσουμε με το όνομά του πρέπει να προσδιορίσουμε ταυτόχρονα και τα ορίσματά του δίνοντας τιμές των αντίστοιχων τύπων σε καθένα από αυτά. Τότε μόνο μπορούν να εκτελεστούν οι εντολές που αντιπροσωπεύει. Όταν ολοκληρωθεί η εκτέλεση του υποπρογράμματος μπορεί να επιστρέφεται τιμή μέσω του ονόματός του και πρέπει να την αποθηκεύσουμε σε κατάλληλη μεταβλητή. Ένα τμήμα κώδικα που θέλουμε να επιστρέφει τιμή το υλοποιούμε ως συνάρτηση ενώ αν δεν επιστρέφει τίποτε στον κώδικά μας αλλά, απλώς εκτελεί κάποιες εντολές το υλοποιούμε ως υπορουτίνα. Αν έχει περισσότερα του ενός αποτελέσματα μπορούμε το υποκαταστήσουμε είτε με συνάρτηση είτε με υπορουτίνα.

Η γενική μορφή της συνάρτησης είναι
επιστρεφόμενος_τύπος FUNCTION όνομα(όρισμαΑ, όρισμαΒ,...)
τύπος_ορίσματος_Α :: όρισμαΑ
τύπος_ορίσματος_Β :: όρισμαΒ

τύπος :: τοπική_μεταβλητή, ...

! κώδικας
......
......
END FUNCTION όνομα
Ισοδύναμα, μπορούμε να περιλάβουμε τη δήλωση του ονόματος (που, μην ξεχνάμε, αποκτά τιμή στο σώμα της συνάρτησης):
FUNCTION όνομα(όρισμαΑ, όρισμαΒ,...)
επιστρεφόμενος_τύπος :: όνομα
τύπος_ορίσματος_Α :: όρισμαΑ
τύπος_ορίσματος_Β :: όρισμαΒ

τύπος :: τοπική_μεταβλητή, ...

! κώδικας
......
......
END FUNCTION όνομα
Έναν τρίτο τρόπο ορισμού, ισοδύναμο με τους παραπάνω, θα τον αναφέρουμε παρακάτω.

Ο τύπος του ονόματος της συνάρτησης μπορεί να είναι οποιοσδήποτε από τους ενσωματωμένους ή όσους ορίζει ο χρήστης (§5.1). Δεν είναι απαραίτητο να είναι απλός τύπος; μπορεί ακόμα να είναι και πίνακας. Επίσης, τα ορίσματα μπορεί να μην υπάρχουν, στη σπάνια περίπτωση που δεν επιθυμούμε να δώσουμε τιμές “εξωτερικά” , οι παρενθέσεις, όμως, πρέπει να υπάρχουν (με κενή λίστα παραμέτρων). Το σώμα της συνάρτησης μπορεί να περιέχει όλες τις εντολές και δηλώσεις που έχουμε ήδη συναντήσει.

Για την υπορουτίνα--που δεν επιστρέφει τιμή--η γενική μορφή ορισμού είναι
SUBROUTINE όνομα(όρισμαΑ, όρισμαΒ,...)
τύπος_ορίσματος_Α :: όρισμαΑ
τύπος_ορίσματος_Β :: όρισμαΒ

τύπος :: μεταβλητή, ...

! κώδικας
......
......
END SUBROUTINE όνομα
Στην περίπτωση που η υπορουτίνα δεν χρειάζεται ορίσματα μπορούν να παραληφθούν οι παρενθέσεις μετά το όνομά της.

===== 6.3 Παραδείγματα =====

Τα παραπάνω θα γίνουν κατανοητά με κάποια παραδείγματα:

Έστω ότι στο πρόγραμμά μας θέλουμε να υπολογίσουμε την τιμή της ποσότητας x2 + x - 9 για x = 1.3 , x = 2.7 , x = - 0.5 . Μπορούμε να έχουμε τον ακόλουθο κώδικα:

PROGRAM fun
IMPLICIT NONE

REAL :: x1, x2, x3, y1, y2, y3

x1 = 1.3
y1 = x1**2 + x1 - 9.0

x2 = 2.7
y2 = x2**2 + x2 - 9.0

x3 = -0.5
y3 = x3**2 + x3 - 9.0

! ......
! ......
END PROGRAM fun

Παρατηρούμε ότι επαναλαμβάνουμε κώδικα, εδώ μία γραμμή, ουσιαστικά χωρίς αλλαγές για κάθε υπολογισμό. Έχουμε τη δυνατότητα να τον “απομονώσουμε” σε μια πιο εύχρηστη συνάρτηση, απλοποιώντας το πρόγραμμά μας; σκεφτείτε πόσο δυσνόητο θα ήταν αν ο επαναλαμβανόμενος κώδικας αποτελείται από μια πολύπλοκη έκφραση ή είναι εκτεταμένος σε πολλές γραμμές κειμένου. Μπορούμε να ορίσουμε το εξής υποπρόγραμμα

FUNCTION polynomial(x)
IMPLICIT NONE

REAL, INTENT (in) :: x
REAL :: polynomial

polynomial = x*x + x - 9.0
END FUNCTION polynomial

Το όνομα της συνάρτησης, polynomial, είναι της επιλογής του προγραμματιστή και σχηματίζεται σύμφωνα με τους κανόνες που ισχύουν για τα ονόματα των μεταβλητών, §1.2.2. Η μία παράμετρος στη συγκεκριμένη συνάρτηση, το μοναδικό όρισμά της, το x, παρατίθεται μετά το όνομα μέσα σε παρενθέσεις. Η συγκεκριμένη συνάρτηση περιέχει όλο το σχετικό κώδικα για τον υπολογισμό του πολυωνύμου. Τα υποπρογράμματα και το κυρίως πρόγραμμα είναι ανεξάρτητα μεταξύ τους και, συνεπώς, όποια ποσότητα χρησιμοποιηθεί από αυτά πρέπει να δηλωθεί σε αυτά. Το όνομα της συνάρτησης πρέπει να δηλωθεί, είτε όπως εδώ, στο τμήμα των δηλώσεων, είτε στην πρώτη γραμμή του κώδικα, ως εξής

REAL FUNCTION polynomial(x)
.....
.....
END FUNCTION polynomial

Παρατηρήστε ότι στον κώδικα της συνάρτησης γίνεται ανάθεση του τελικού αποτελέσματος, αυτού που θέλουμε να επιστρέψουμε, στο όνομά της. Το όνομα της συνάρτησης δηλώνεται και συμπεριφέρεται όπως μια οποιαδήποτε μεταβλητή. Βέβαια, δεν έχει αρχική τιμή και πρέπει να αποκτήσει κάποια προτού τελειώσει η εκτέλεση της συνάρτησης.

Απομένει να εξηγήσουμε την ιδιαίτερη δήλωση της παραμέτρου. Προφανώς, ως ποσότητα που χρησιμοποιείται στη συνάρτηση, πρέπει να δηλωθεί, να ενημερωθεί ο μεταγλωττιστής ότι εδώ το σύμβολο x αντιστοιχεί σε πραγματικό αριθμό. Όπως βλέπουμε, η δήλωση της παραμέτρου συμπληρώνεται με το INTENT (in) . Το συγκεκριμένο υποδηλώνει την πρόθεσή μας να χρησιμοποιήσουμε τη μεταβλητή μόνο για ανάγνωση. Παρατηρούμε ότι δεν την τροποποιούμε στον κώδικα και συνεπώς είναι καλό να ενημερώνουμε τον μεταγλωττιστή και τον χρήστη του υποπρογράμματος γι' αυτό. Στην περίπτωση που δε μας ενδιαφέρει η τιμή που θα έχει το σύμβολο αυτό όταν κληθεί η συνάρτηση αλλά θα του αποδόσουμε τιμή, μόνο, η δήλωση (καλό είναι να) συμπληρώνεται με το INTENT (out) . Στην περίπτωση που χρησιμοποιούμε και τροποποιούμε την τιμή της παραμέτρου στη συνάρτηση, συμπληρώνουμε τη δήλωση με το INTENT (inout) . Πάντως, ένας από τους τρεις προσδιορισμούς της χρήσης καλό είναι να υπάρχει σε κάθε δήλωση ορίσματος.

Τον κώδικα που ορίζει τη συνάρτηση τον γράφουμε πριν ή μετά το κυρίως πρόγραμμα, στο ίδιο αρχείο. Εναλλακτικά, μπορούμε να τον έχουμε σε ξεχωριστό αρχείο.

Προτού εξηγήσουμε πώς χρησιμοποιούμε το υποπρόγραμμα, ας δούμε ένα ακόμα, πιο σύνθετο, παράδειγμα:

Γνωρίζουμε από τα μαθηματικά ότι

ln(1+x) =-∑k=1∞ (-x)k/k, -1 ≤ x ≤ 1.
Έστω ότι θέλουμε να υπολογίσουμε την τιμή του ln(1.05) με το συγκεκριμένο τύπο και όχι με την ενσωματωμένη συνάρτηση ( LOG() ). Φυσικά, δεν μπορούμε να συγκεντρώσουμε άπειρους όρους και έτσι σταματούμε τη σειρά σε κάποιον όρο που είναι αρκετά μικρός κατ' απόλυτη τιμή και δεν συνεισφέρει στην τελική τιμή· όλοι οι υπόλοιποι είναι μικρότεροί του. Ο κώδικας θα είναι

PROGRAM logarithm
IMPLICIT NONE
REAL :: sum, y, x, term
INTEGER :: k

! PART A
! READ
DO
READ *, y
x = y - 1.0
IF ( (-1.0 <= x) .AND. ( x <= 1.0 ) ) EXIT
END DO

! PART B
! COMPUTE
k = 1
sum = 0.0
DO
term = (-x)**k / k
IF (ABS(term) < 1e-6) EXIT
sum = sum + term
k = k + 1
END DO

! PART C
! PRINT
PRINT *, "ln(", y, ") = ", -sum, LOG(y)
END PROGRAM logarithm

Παρατηρούμε ότι ο κώδικας αποτελείται από τρία τμήματα:
την ανάγνωση και αποδοχή της τιμής,
τον υπολογισμό της ποσότητας που θέλουμε και
την εκτύπωση του αποτελέσματος.
Αντί να έχουμε όλες τις εντολές συγκεντρωμένες και να τις ξεχωρίζουμε μόνο με σχόλια μπορούμε να απομονώσουμε τμήματα του προγράμματος σε υποπρογράμματα. Ας δούμε πώς:

Παρατηρούμε ότι ο υπολογισμός του sum είναι το (μόνο χρήσιμο) αποτέλεσμα του κώδικα μεταξύ των !PART A και !PART B . Μπορούμε να ορίσουμε μια συνάρτηση που να περιέχει μόνο τις σχετικές εντολές και να επιστρέφει την τιμή του sum:

FUNCTION sumseries(x)
IMPLICIT NONE

REAL, INTENT (in) :: x
REAL :: sumseries

REAL :: term, sum
INTEGER :: k

k = 1
sum = 0.0
DO
term = (-x)**k / k
IF (ABS(term) < 1e-6) EXIT
sum = sum + term
k = k + 1
END DO
sumseries = sum
END FUNCTION sumseries

Η δήλωση της επιστρεφόμενης ποσότητας, του ονόματος της συνάρτησης δηλαδή, μπορούσε να γίνει ως εξής

REAL FUNCTION sumseries(x)
.....
.....
END FUNCTION sumseries

Παρατηρήστε ότι συμπληρώσαμε τον κώδικα με την ανάθεση του τελικού αποτελέσματος στο όνομα της συνάρτησης.

Ένα άλλο, αρκετά ανεξάρτητο τμήμα του προγράμματος που μπορούμε να απομονώσουμε, είναι ο κώδικας για την ανάγνωση και αποδοχή τιμής. Βλέπουμε ότι δίνει τιμή σε δύο μεταβλητές που χρησιμοποιούνται στο υπόλοιπο πρόγραμμα. Μπορούμε να τον γράψουμε σε ξεχωριστή συνάρτηση που θα επιστρέφει τη μία τιμή (θα ανατίθεται, δηλαδή, στο όνομα της συνάρτησης) ενώ η άλλη ποσότητα θα αποδίδεται σε όρισμα. Έτσι θα έχουμε

FUNCTION readxy(y)
IMPLICIT NONE
REAL, INTENT (out) :: y
REAL :: readxy

REAL :: x

DO
READ *, y
x = y - 1.0
IF ( (-1.0 <= x) .AND. ( x <= 1.0 ) ) EXIT
END DO

readxy = x
END FUNCTION readxy

Προσέξτε ότι η δήλωση της μεταβλητής y περιλαμβάνει τον προσδιορισμό INTENT (out) , ο οποίος υποδηλώνει ότι το όρισμα y παίρνει τιμή από τη συνάρτηση και ότι η τυχόν αρχική τιμή του δεν χρησιμοποιείται. Παρατηρήστε, ακόμα, την απόδοση τιμής στο όνομα της συνάρτησης.

Η συγκεκριμένη επιλογή για την δημιουργία του υποπρογράμματος δεν είναι λάθος; όμως, αντιμετωπίζει διαφορετικά δύο όμοιες, ισοδύναμες ποσότητες. Ας δούμε μια άλλη προσέγγιση που δεν παρουσιάζει αυτήν την ανομοιομορφία, την υλοποίηση με υπορουτίνα:

SUBROUTINE readxy(x,y)
IMPLICIT NONE
REAL, INTENT (out) :: x, y

DO
READ *, y
x = y - 1.0
IF ( (-1.0 <= x) .AND. ( x <= 1.0 ) ) EXIT
END DO
END SUBROUTINE readxy

===== 6.4 Kλήση υποπρογράμματος =====

Η κλήση μιας συνάρτησης γίνεται, οπουδήποτε επιθυμούμε, χρησιμοποιώντας το όνομά της ακολουθούμενο από κατάλληλες τιμές εντός παρενθέσεων, αντίστοιχες σε πλήθος και τύπο με τα ορίσματά της6.1. Καθώς επιστρέφει τιμή, μπορεί να εμφανίζεται μόνο σε εκφράσεις που συμμετέχουν και άλλες ποσότητες, σε εντολή ανάθεσης ή, γενικότερα, σε εντολή που “περιμένει” ποσότητα του τύπου που επιστρέφει η συνάρτηση. Έτσι, π.χ. η συνάρτηση polynomial( ) που ορίσαμε παραπάνω μπορεί να χρησιμοποιηθεί σε κώδικα ως εξής:

y = polynomial(1.3)

ή

y = polynomial(2.4) + 2.0 * polynomial(3.5) + 4.0

ή, ακόμα,

PRINT *, polynomial(4.6)

αλλά, βέβαια, όχι σε μια εντολή από μόνη της ή σε εντολή ανάθεσης σε αυτή, ή χωρίς να ακολουθείται από τα ορίσματά της6.2:

polynomial(3.4) ! error
polynomial(3.4) = 4.0 ! error
y=polynomial ! error

Κλήση μιας υπορουτίνας γίνεται με αρκετά διαφορετικό τρόπο. Και σε αυτήν την περίπτωση χρησιμοποιούμε το όνομά της ακολουθούμενο από κατάλληλες τιμές, εντός παρενθέσεων, αντίστοιχες με τα ορίσματά της. Όμως, προηγείται η δεσμευμένη λέξη CALL και η κλήση αποτελεί ξεχωριστή εντολή. Η υπορουτίνα readxy( ) που ορίσαμε παραπάνω μπορεί να κληθεί ως εξής (μόνο!)

REAL :: x, y

CALL readxy(x,y)

Παρατηρήστε ότι ως ορίσματα δίνουμε δύο μεταβλητές. Καθώς η συγκεκριμένη υπορουτίνα τροποποιεί τις παραμέτρους της, δεν μπορεί να έχει ως ορίσματα σταθερές ποσότητες. Μετά την κλήση, τα x,y έχουν πάρει κατάλληλες τιμές και μπορούμε να τα χρησιμοποιήσουμε.

''6.4.1 Δήλωση υποπρογράμματος''

Η κλήση μιας συνάρτησης ή υπορουτίνας μπορεί να γίνει σε οποιοδήποτε σημείο του κώδικά μας, είτε στο κυρίως πρόγραμμα, είτε σε άλλο υποπρόγραμμα (όχι, όμως, στον εαυτό τους)6.3. Όμως, όπως έχουμε ήδη πει και θα τονίσουμε άλλη μια φορά, οποιαδήποτε ποσότητα στο πρόγραμμά μας, μεταβλητή, σταθερή, πίνακας ή συνάρτηση, προτού χρησιμποιηθεί πρέπει να δηλωθεί, ώστε να ενημερωθεί ο μεταγλωττιστής για το είδος της, τον τύπο της ή, πιθανόν, την τιμή της. Η απαραίτητη δήλωση μιας συνάρτησης ή υπορουτίνας γίνεται ως εξής: στο κυρίως πρόγραμμα ή στο υποπρόγραμμα που θέλουμε να τη χρησιμοποιήσουμε, συμπληρώνουμε το τμήμα των δηλώσεων με το interface (διασύνδεση) του υποπρογράμματος που θα κληθεί. Το interface είναι ό,τι απομένει από τον ορισμό του υποπρογράμματος αν παραλείψουμε τις εντολές που αποτελούν το σώμα του και τις δηλώσεις των ποσοτήτων που χρησιμοποιούνται σε αυτές. Επομένως, αποτελείται από την πρώτη γραμμή του ορισμού (που εισάγει το υποπρόγραμμα), τις δηλώσεις των ορισμάτων και την τελευταία γραμμή (το END ). Π.χ. το interface της συνάρτησης polynomial( ) που ορίσαμε παραπάνω είναι:

FUNCTION polynomial(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: polynomial
END FUNCTION polynomial

ενώ της υπορουτίνας readxy( ) είναι:

SUBROUTINE readxy(x,y)
IMPLICIT NONE
REAL, INTENT (out) :: x, y
END SUBROUTINE readxy

Τα interfaces των υποπρογραμμάτων που θα κληθούν στο κυρίως πρόγραμμα ή σε υποπρόγραμμα περιλαμβάνονται σε INTERFACE ... END INTERFACE .

===== 6.5 Παράδειγμα =====

Τα παραπάνω θα γίνουν κατανοητά τροποποιώντας το κυρίως πρόγραμμα (logarithm), στο §6.3. Πρώτα, αν και δεν έχει σημασία η σειρά, παραθέτουμε ξανά τη συνάρτηση sumseries( ) και το υποπρόγραμμα readxy( ). Κατόπιν, γράφουμε αλλαγμένο το κυρίως πρόγραμμα:

FUNCTION sumseries(x)
IMPLICIT NONE

REAL, INTENT (in) :: x
REAL :: sumseries

REAL :: term, sum
INTEGER :: k

k = 1
sum = 0.0
DO
term = (-x)**k / k
IF (ABS(term) < 1e-6) EXIT
sum = sum + term
k = k + 1
END DO
sumseries = sum
END FUNCTION sumseries

SUBROUTINE readxy(x,y)
IMPLICIT NONE
REAL, INTENT (out) :: x, y

DO
READ *, y
x = y - 1.0
IF ( (-1.0 <= x) .AND. ( x <= 1.0 ) ) EXIT
END DO
END SUBROUTINE readxy

PROGRAM logarithm
IMPLICIT NONE
REAL :: sum, y, x, term
INTEGER :: k

INTERFACE
FUNCTION sumseries(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: sumseries
END FUNCTION sumseries

SUBROUTINE readxy(x,y)
IMPLICIT NONE
REAL, INTENT (out) :: x, y
END SUBROUTINE readxy
END INTERFACE

CALL readxy(x,y) ! READ

sum = sumseries(x) ! COMPUTE

PRINT *, "ln(", y, ") = ", -sum, LOG(y) ! PRINT
END PROGRAM logarithm

Παρατηρούμε ότι το κυρίως σώμα του προγράμματός μας απλοποιήθηκε σημαντικά. Εκτός από αυτήν τη βελτίωση, μπορούμε να επεκτείνουμε το πρόγραμμα επαναλαμβάνοντας τις κλήσεις των δυο υποπρογραμμάτων όσες φορές επιθυμούμε χωρίς να επαναλαμβάνουμε τον κώδικά τους, όπως θα ήμασταν αναγκασμένοι να κάνουμε χωρίς τη χρήση των υποπρογραμμάτων.

===== 6.6 Εκτέλεση υποπρογράμματος-- RETURN =====

Ένα υποπρόγραμμα εκτελείται αν, και μόνο αν, κληθεί από άλλο τμήμα του προγράμματός μας. Στο σημείο της κλήσης, η ροή της εκτέλεσης των εντολών συνεχίζει στο σώμα του καλούμενου υποπρογράμματος μέχρι να συναντήσει το καταληκτικό END FUNCTION / END SUBROUTINE ή την εντολή RETURN . Τότε, η ροή επιστρέφει στο σημείο που έγινε η κλήση και συνεχίζει με την επόμενη εντολή.

Οι ποσότητες που ορίζονται με το γνωστό τρόπο σε ένα υποπρόγραμμα δημιουργούνται όταν συναντήσει η ροή εκτέλεσης τη δήλωσή τους και καταστρέφονται όταν η ροή εγκαταλείψει το υποπρόγραμμα.

===== 6.7 Όρισμα υποπρογράμματος =====

Όπως αναφέραμε, το όνομα ενός υποπρογράμματος ακολουθείται κατά τον ορισμό του από καμία, μία ή περισσότερες συμβολικές ποσότητες εντός παρενθέσεων. Αυτές είναι τα ορίσματα, οι παράμετροι του κώδικα που αντιπροσωπεύει το υποπρόγραμμα; ο κώδικας δεν μπορεί να εκτελεστεί αν δε δοθούν τιμές σε αυτά. Τιμές αποδίδουμε στα ορίσματα κατά την κλήση του υποπρογράμματος, με πλήθος, σειρά και τύπο όπως ακριβώς προσδιορίζεται στον ορισμό του υποπρογράμματος.

Ειδικές περιπτώσεις που χρειάζονται εξήγηση είναι οι ακόλουθες.

''6.7.1 Πίνακας ως όρισμα''

Όρισμα ενός υποπρογράμματος μπορεί φυσικά να είναι ένας πίνακας. Ας δούμε τους τρόπους δήλωσής του.

Όταν το υποπρόγραμμα πρόκειται να χρησιμοποιηθεί για πίνακα με διαστάσεις σταθερές και γνωστές κατά τη μεταγλώττιση, (ή πίνακες με ίδιο σχήμα και διαστάσεις) η δήλωση του αντίστοιχου ορίσματος είναι η αναμενόμενη. Π.χ. η υπορουτίνα

SUBROUTINE abc(a)
IMPLICIT NONE
REAL, INTENT (in) :: a(3)

PRINT *, a(1) + a(2) + a(3)
END SUBROUTINE abc

δέχεται ως όρισμα έναν μονοδιάστατο πίνακα 3 πραγματικών στοιχείων.

Αν επιθυμούμε να χρησιμοποιήσουμε το υποπρόγραμμά μας για πίνακες με οποιεσδήποτε διαστάσεις, αλλά πάντα με ίδιο σχήμα (μονοδιάστατους, διδιάστατους, κλπ.), μπορούμε να δηλώσουμε το αντίστοιχο όρισμα χωρίς να προσδιορίσουμε αριθμητικά τις διαστάσεις, αλλά μόνο το σχήμα χρησιμοποιώντας κατάλληλο πλήθος των χαρακτήρων (Smiling. Έτσι, το άθροισμα οποιουδήποτε διδιάστατου πίνακα μπορεί να εκτυπωθεί με την ακόλουθη υπορουτίνα

SUBROUTINE def(a)
IMPLICIT NONE
REAL, INTENT (in) :: a(:,Smiling

PRINT *, SUM(a)
END SUBROUTINE def

Η κλήση της συγκεκριμένης χρειάζεται μόνο το όνομα ενός διδιάστατου πίνακα για όρισμα. Όποτε χρειαζόμαστε τις διαστάσεις του συγκεκριμένου ορίσματος μπορούμε να τις βρούμε με τη συνάρτηση SIZE( ) . Έτσι, η δημιουργία ενός τοπικού πραγματικού πίνακα με ίδιες διαστάσεις με το εκάστοτε όρισμα γίνεται με την ακόλουθη δήλωση (στο υποπρόγραμμα)

REAL :: b(SIZE(a,1), SIZE(a,2))

Εναλλακτικά (χωρίς να υπάρχει κανένα πλεονέκτημα), μπορούμε να περάσουμε και τις διαστάσεις του πίνακα ως ορίσματα και να τις χρησιμοποιήσουμε στη δήλωση. Έτσι, αν το όρισμα είναι ένας διδιάστατος πίνακας a(m,n) ο ορισμός γίνεται

SUBROUTINE def(a, m, n)
IMPLICIT NONE
INTEGER, INTENT (in) :: m,n
REAL, INTENT (in) :: a(m,n)

PRINT *, SUM(a)
END SUBROUTINE def

Φυσικά, η κλήση του υποπρογράμματος αλλάζει αντίστοιχα.

''6.7.2 Υποπρόγραμμα ως όρισμα''

Εκτός από απλές μεταβλητές ενσωματωμένου ή παραγόμενου τύπου και πίνακες μπορούμε να έχουμε ως όρισμα άλλο υποπρόγραμμα. Ο τρόπος που γίνεται αυτό είναι απλός: στη λίστα των ορισμάτων αναφέρουμε μόνο το όνομα της συνάρτησης ή της υπορουτίνας. Στις δηλώσεις των ορισμάτων πρέπει να περιλάβουμε τη “δήλωση” του υποπρογράμματος, δηλαδή το INTERFACE του.

Φυσικά, όπως ισχύει και για όλα τα ορίσματα, το όνομα του υποπρογράμματος που είναι όρισμα, είναι ένα τοπικής χρήσης σύμβολο. Το ποια συνάρτηση ή υπορουτίνα θα χρησιμοποιηθεί καθορίζεται κατά την κλήση. Τότε πρέπει στη θέση του αντίστοιχου ορίσματος να δώσουμε το όνομα ενός υποπρογράμματος ίδιου είδους (συνάρτηση ή υπορουτίνα) με το όρισμα, και με ίδιο πλήθος και τύπο ορισμάτων και τυχόν επιστρεφόμενης τιμής. Παράδειγμα χρήσης είναι το ακόλουθο:

SUBROUTINE plot(a, b, f)
IMPLICIT NONE
REAL, INTENT (in) :: a, b

INTERFACE
FUNCTION f(x)
IMPLICIT NONE
REAL, intent (in) :: x
REAL :: f
END FUNCTION f
END INTERFACE

INTEGER :: i
REAL :: x, step

step = (b-a) / 100.0

DO i=0,100
x = a + i * step
PRINT *, x, f(x)
END DO
END SUBROUTINE plot

FUNCTION g(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: g

g = x*x - 2.0
END FUNCTION g

FUNCTION h(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: h

h = 1.0 - 2.0 * x
END FUNCTION h

PROGRAM test
IMPLICIT NONE
INTERFACE
SUBROUTINE plot(a, b, f)
IMPLICIT NONE
REAL, INTENT (in) :: a, b

INTERFACE
FUNCTION f(x)
IMPLICIT NONE
REAL, intent (in) :: x
REAL :: f
END FUNCTION f
END INTERFACE
END SUBROUTINE plot

FUNCTION g(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: g
END FUNCTION g

FUNCTION h(x)
IMPLICIT NONE
REAL, INTENT (in) :: x
REAL :: h
END FUNCTION h
END INTERFACE

CALL plot(0.0, 1.0, g)
CALL plot(0.0, 1.0, h)

END PROGRAM test

Στον παραπάνω κώδικα έχουμε τη συνάρτηση plot που τυπώνει στην οθόνη τις τιμές της συνάρτησης που δέχεται ως όρισμα, f, σε 101 ισαπέχουσες τιμές μεταξύ των a,b που και αυτά είναι ορίσματα. Τη χρησιμοποιούμε για να τυπώσουμε τιμές για δύο συναρτήσεις g(x), h(x) μεταξύ των 0.0 , 1.0

Παρατήρηση: Οι ενσωματωμένες συναρτήσεις πρέπει να δοθούν με ειδικό όνομα ανάλογα με τον τύπο των ορισμάτων τους. Τούτο αποτελεί κατάλοιπο της Fortran 66 που έχει κληρονομηθεί στις επόμενες εκδόσεις; δεν έχουμε αναφερθεί σε αυτά τα ονόματα.

''6.7.3 Προαιρετικό όρισμα''

Η Fortran μας δίνει τη δυνατότητα να προσδιορίσουμε ότι κάποια από τα ορίσματα είναι προαιρετικά, συμπληρώνοντας τη δήλωσή τους με τη δεσμευμένη λέξη OPTIONAL . Τα συγκεκριμένα ορίσματα πρέπει να βρίσκονται στο τέλος της λίστας ορισμάτων. Κατά την κλήση μπορούμε να δώσουμε τιμή σε αυτά κανονικά, ή να παραλείψουμε να το κάνουμε. Στην τελευταία περίπτωση δεν μπορούμε να τα χρησιμοποιήσουμε στο σώμα του υποπρογράμματος. Η συνάρτηση PRESENT( ) παρέχεται από την Fortran για να ελέγχει αν το όρισμά της, που πρέπει να είναι ένα από τα προαιρετικά ορίσματα του καλώντος υποπρογράμματος, έχει τιμή. Αν κατά την κλήση του υποπρογράμματος, το προαιρετικό όρισμα π.χ. a αποκτά τιμή, η τιμή του PRESENT(a) είναι .TRUE. . Αντίστοιχα, το PRESENT(a) είναι .FALSE. αν το όρισμα δεν περιλαμβάνεται στην κλήση.

Παράδειγμα της δυνατότητας που περιγράψαμε είναι το ακόλουθο.

FUNCTION f(x,a)
IMPLICIT NONE
REAL, INTENT(in) :: x
REAL, INTENT (in), OPTIONAL :: a
REAL :: f

IF (PRESENT(a)) THEN
f = x + a
ELSE
f = x + 10.0
ENDIF

END FUNCTION f

Στον παραπάνω ορισμό, το δεύτερο όρισμα της f( ) είναι προαιρετικό. Αν υπάρχει, χρησιμοποιείται για να υπολογιστεί η επιστρεφόμενη τιμή; στην αντίθετη περίπτωση, ο υπολογισμός γίνεται χωρίς αυτή.

Η κλήση της f( ) μπορεί να γίνει με δύο τρόπους, όπως στον παρακάτω κώδικα

PROGRAM a
IMPLICIT NONE
INTERFACE
FUNCTION f(x,a)
IMPLICIT NONE
REAL, INTENT(in) :: x
REAL, INTENT (in), OPTIONAL :: a
REAL :: f
END FUNCTION f
END INTERFACE

PRINT *, f(3.0)
PRINT *, f(3.0, 20.0)

END PROGRAM a

===== 6.8 Στατικές ποσότητες =====

Αναφέραμε ότι οι μεταβλητές που ορίζονται στο σώμα ενός υποπρογράμματος έχουν διάρκεια ζωής όση και η διάρκεια εκτέλεσης του υποπρογράμματος.

Μπορούμε να ορίσουμε κατάλληλα κάποια μεταβλητή έτσι ώστε να δημιουργηθεί μόνο την πρώτη φορά που η ροή θα συναντήσει τη δήλωσή της και, επιπλέον, να μην καταστραφεί κατά την έξοδο από το υποπρόγραμμα (και να διατηρήσει, επομένως, την τιμή της). Αυτό γίνεται προσθέτοντας στον ορισμό της μεταβλητής την προκαθορισμένη λέξη SAVE ή, ισοδύναμα, δίνοντας αρχική τιμή κατά τη δήλωση. Έτσι στην υπορουτίνα

SUBROUTINE f(a)
IMPLICIT NONE
REAL, INTENT (in) :: a

INTEGER, SAVE :: k = 0

k = k + 1

! ...
END SUBROUTINE f

η μεταβλητή k ουσιαστικά μετρά πόσες φορές κλήθηκε το υποπρόγραμμα. Εννοείται ότι “φαίνεται” μόνο μέσα στην f( ).

Η δημιουργία μια μεταβλητής με SAVE και η τυχόν ανάθεση αρχικής τιμής γίνεται την πρώτη φορά που εκτελείται το υποπρόγραμμα. Σε κάθε επόμενη κλήση, η δήλωση και η απόδοση τιμής αγνοούνται.

===== 6.9 Αναδρομική (recursive) κλήση συνάρτησης =====

Πολύ συχνά συναντούμε προβλήματα που ορίζονται αναδρομικά. Π.χ. ο ορισμός του παραγοντικού ενός ακεραίου είναι n!= 1 x 2 x ... x (n-1) x n=(n-1)! x n, n > 0,
1, n = 0.

Παρατηρούμε ότι για να υπολογίσουμε το παραγοντικό ενός αριθμού n με τον συγκεκριμένο τύπο, πρέπει να υπολογίσουμε το παραγοντικό ενός άλλου αριθμού (του n - 1 ). Η υλοποίηση αυτού του τύπου σε μια γλώσσα προγραμματισμού απαιτεί μια συνάρτηση που, προτού ολοκληρωθεί, καλεί τον εαυτό της; για να βγάλει, δηλαδή, αποτέλεσμα πρέπει να χρησιμοποιήσει την ίδια. Φυσικά, η κλήση αυτή πρέπει να γίνεται υπό κάποια συνθήκη, αλλιώς δε θα επιστραφεί ποτέ τιμή.

6.9.0.0.1 Παράδειγμα:

Ας δούμε πώς μπορούμε να γράψουμε μια συνάρτηση για το παραγοντικό ενός ακεραίου με αναδρομικό (recursive) τρόπο στη Fortran. Η πρώτη απόπειρα θα μπορούσε να είναι ως εξής

FUNCTION par(n) ! not correct
IMPLICIT NONE
INTEGER, INTENT (in) :: n
INTEGER :: par

IF (n == 0) THEN
par = 1
ELSE
par = par(n-1) * n
ENDIF
END FUNCTION par

Στη συνάρτηση αυτή έχουμε αγνοήσει τους ελέγχους που κανονικά πρέπει να γίνονται (το n να μην είναι αρνητικό και το αποτέλεσμα να μπορεί να αναπαρασταθεί στον επιστρεφόμενο τύπο). Παρατηρήστε ότι το σώμα της συνάρτησης είναι πιστή μεταγραφή του μαθηματικού τύπου σε κώδικα Fortran, και αρκετά πιο απλή από την εναλλακτική υλοποίηση που έχουμε δει ως τώρα. Προσέξτε ότι η κλήση της par( ) στο σώμα της δεν είναι ανεξέλεγκτη· η ακολουθία par(n) → par(n-1) → par(n-2) →... σταματά (και επιστρέφεται τιμή που υπολογίζεται χωρίς την κλήση της) όταν το όρισμα γίνει 0.

Δυστυχώς, ο ορισμός αναδρομικής συνάρτησης στη Fortran δεν μπορεί να γίνει με το γνωστό τρόπο που περιγράψαμε στο παρόν κεφάλαιο (χωρίς να υπάρχει ουσιαστικός λόγος). Ο σωστός ορισμός για τη συνάρτηση του παραγοντικού γράφεται ως εξής

RECURSIVE FUNCTION par(n) RESULT (par1)
IMPLICIT NONE
INTEGER, INTENT (in) :: n
INTEGER :: par1

IF (n == 0) THEN
par1 = 1
ELSE
par1 = par(n-1) * n
ENDIF
END FUNCTION par

Προσέξτε ότι περιλάβαμε τη δεσμευμένη λέξη RECURSIVE (αναδρομικός) στον ορισμό και γράφηκε χωριστά το αποτέλεσμα της συνάρτησης (μια νέα μεταβλητή με όνομα της επιλογής μας) από το όνομα της συνάρτησης. Πλέον, γίνεται δήλωση της μεταβλητής που αντιστοιχεί στο αποτέλεσμα, και σε αυτή τη μεταβλητή γίνεται ανάθεση τιμής. Το όνομα της συνάρτησης δε δηλώνεται; με αυτό όμως γίνεται η κλήση της.

Αν εξαιρέσουμε τον λίγο πολύπλοκο τρόπο δήλωσης (που μπορεί να χρησιμοποιηθεί και για τις μη αναδρομικές συναρτήσεις, παραλείποντας φυσικά το RECURSIVE ), η υποστήριξη από τη Fortran 90 του αναδρομικού μηχανισμού κλήσης αποτελεί μια σημαντική προσθήκη στις δυνατότητες της γλώσσας σε σχέση με τη Fortran 77.

===== 6.10 Υποπρογράμματα κατά στοιχείο ( ELEMENTAL ) =====

Όπως έχουμε περιγράψει, τα υποπρογράμματα κατά την κλήση τους απαιτούν να δίνονται τιμές στα ορίσματά τους κατάλληλου τύπου. Δεν είναι δυνατόν σε υποπρόγραμμα που περιμένει π.χ. ακέραια μεταβλητή να δώσουμε πραγματική, ή πίνακα ή σύνθετο τύπο. Όμως, οι ενσωματωμένες συναρτήσεις και υπορουτίνες της Fortran έχουν την ιδιότητα να δέχονται και απλές μεταβλητές και πίνακες. Έτσι, μπορούμε να έχουμε

REAL :: x, y
x = 5.0
y = SQRT(x)

αλλά και

REAL, DIMENSION (100) :: x, y
x(1) = ....
x(2) = ....
...
x(100) = ....

y = SQRT(x)

Η τελευταία εντολή ισοδυναμεί με

y(1) = SQRT(x(1))
y(2) = SQRT(x(2))
...
y(100) = SQRT(x(100))

Αυτήν την ιδιότητα μπορούμε να την επεκτείνουμε και για τα υποπρογράμματα που ορίζει ο χρήστης αν συμπληρώσουμε τη δήλωση μιας υπορουτίνας ή συνάρτησης που έχει μόνο απλά ορίσματα με τη δεσμευμένη λέξη ELEMENTAL . Π.χ. η συνάρτηση

ELEMENTAL FUNCTION myexp(x)
IMPLICIT NONE

REAL, INTENT (in) :: x
REAL :: myexp
INTEGER :: i
REAL :: term
myexp = 0.0
term = 1.0
DO i = 0, 10
myexp = myexp + term
term = term * x / (i+1)
END DO
END FUNCTION myexp

μπορεί να δεχτεί ως όρισμα είτε ένα πραγματικό αριθμό ή μεταβλητή, είτε ένα πραγματικό πίνακα. Το αποτέλεσμα θα είναι αντίστοιχα ένας πραγματικός αριθμός ή ένας πίνακας πραγματικών αριθμών με την ίδια διάσταση και σχήμα όπως ο πίνακας στο όρισμα.

Μια συνάρτηση μπορεί να γίνει ELEMENTAL αν όλα τα ορίσματα έχουν προσδιοριστεί με το INTENT (in) , δεν έχει στατικές μεταβλητές ( SAVE ), δεν περιέχει εντολή STOP και δεν γράφει σε ή διαβάζει από εξωτερικό αρχείο. Επιπλέον, όλα τα ορίσματά της πρέπει να είναι απλές μεταβλητές.

===== 6.11 Module =====

Ας τονίσουμε σε αυτό το σημείο ότι ποσότητες με το ίδιο όνομα, δηλωμένες σε διαφορετικά υποπρογράμματα (ή στο κυρίως πρόγραμμα), είναι τελείως ανεξάρτητες, δεν έχουν καμμία σχέση μεταξύ τους. Επιπλέον, δεν υπάρχει τρόπος ώστε ένα υποπρόγραμμα να χρησιμοποιήσει ποσότητα που ορίζεται σε άλλο τμήμα του κώδικά μας. Αν επιθυμούμε κάτι τέτοιο θα πρέπει να περάσουμε τη συγκεκριμένη ποσότητα ως όρισμα στο υποπρόγραμμα.

Συχνά υπάρχει η ανάγκη να χρησιμοποιήσουμε μια ποσότητα σε πολλά υποπρογράμματα (ή και στο κυρίως πρόγραμμα). Οι τιμές μαθηματικών ή φυσικών σταθερών (π.χ. η τιμή του π ), οι παράμετροι του προβλήματός μας (πλήθος αντικειμένων που περιγράφονται από τον κώδικά μας, διαστάσεις πινάκων κ.λ.π.), οι δηλώσεις παραγόμενων τύπων κ.λ.π., είναι απαραίτητο, σύμφωνα με όσα είπαμε, να επαναλαμβάνονται, οριζόμενες σε κάθε υποπρόγραμμα που θα τις προσπελάσει. Οποιαδήποτε αλλαγή στην τιμή τους ή στον τύπο τους (π.χ. για αύξηση της ακρίβειας με την οποία ορίζονται οι σταθερές) πρέπει φυσικά να γίνει σε όλα τα σημεία στα οποία αυτές ορίζονται. Σε μια τέτοια διαδικασία είναι πολύ εύκολο να γίνει λάθος ή να μην γίνει παντού η τροποποίηση.

Το τέταρτο στοιχείο ομαδοποίησης κώδικα, μετά το κυρίως πρόγραμμα, την συνάρτηση και την υπορουτίνα, το module, διευκολύνει πολύ όταν θέλουμε να ορίσουμε μια ποσότητα μία φορά και να τη χρησιμοποιήσουμε σε πολλά σημεία. Συντάσσεται ως εξής: στην αρχή του κώδικά μας, έξω από το κυρίως πρόγραμμα ή τα υποπρογράμματα και πριν την πρώτη χρήση του, γράφουμε

MODULE onoma

END MODULE onoma

Το όνομά του επιλέγεται σύμφωνα με τους γνωστούς κανόνες που ισχύουν για τα ονόματα των μεταβλητών, §1.2.2. Στο εσωτερικό του μπορούμε να περιλάβουμε δηλώσεις σταθερών και μεταβλητών ποσοτήτων αλλά όχι κώδικα. Κατόπιν, σε κάθε υποπρόγραμμα (ή στο κυρίως πρόγραμμα) που χρειαζόμαστε τις συγκεκριμένες ποσότητες περιλαμβάνουμε την εντολή

USE onoma

πριν από τις δηλώσεις οποιασδήποτε ποσότητας (και πριν το IMPLICIT NONE ). Με άλλα λόγια, η συγκεκριμένη εντολή ακολουθεί το PROGRAM ... ή το FUNCTION ... ή το SUBROUTINE ... . Το τμήμα του προγράμματός μας που κάνει “χρήση” ενός module αποκτά πρόσβαση σε όλες τις ποσότητες που ορίζονται στο σχετικό MODULE 6.4.

Παράδειγμα χρήσης είναι το ακόλουθο:

MODULE constants
IMPLICIT NONE

REAL, PARAMETER :: pi = 3.14159
END MODULE constants

PROGRAM mainprog
USE constants

IMPLICIT NONE
!.....

! use pi
END PROGRAM mainprog

FUNCTION f(x)
USE constants

IMPLICIT NONE
!.....

! use pi
END FUNCTION f

Όπως αναφέραμε, ένα module μπορεί να περιέχει και δηλώσεις ποσοτήτων που δεν είναι σταθερές. Αποτελεί, επομένως, ένα μηχανισμό για να μεταφέρουμε τιμές από ένα υποπρόγραμμα σε κάποιο άλλο. Καθώς οι μεταβλητές ενός module είναι προσβάσιμες από όσα υποπρογράμματα κάνουν “χρήση” αυτού, υπάρχει η δυνατότητα κάποιο από αυτά να “γράφει” τιμές σε μια κοινή μεταβλητή και κάποιο άλλο να τη “διαβάζει” .

Συμπερασματικά, υπάρχουν δύο τρόποι για μεταφορά πληροφορίας μεταξύ διαφορετικών τμημάτων του κώδικά μας: είτε μέσω ορισμάτων είτε μέ τη χρήση module. Προσέξτε όμως ότι το INTERFACE ενός υποπρογράμματος αρκεί για να προσδιορίσουμε αν η μεταβλητή που του “περνάμε” ως όρισμα τροποποιείται. Αντίθετα, μόνο με την εξέταση των εντολών του υποπρογράμματος (που μπορεί να βρίσκεται σε άλλο αρχείο) πληροφορούμαστε για το ποιες μεταβλητές που “περνούν” μέσω module αλλάζουν. Ο εντοπισμός σφαλμάτων σε ένα μεγάλο πρόγραμμα γίνεται πιο απλός όσο πιο εύκολα μπορούμε να προσδιορίσουμε ποιες μεταβλητές αλλάζουν και πού. Επομένως, η χρήση των modules προτείνεται για τη “διάδοση” σταθερών ποσοτήτων ενώ οι μεταβλητές ποσότητες είναι προτιμότερο να μεταφέρονται με ορίσματα.

Η εισαγωγή του module στη Fortran 90 επιτρέπει την υποστήριξη του “προγραμματισμού βασιζόμενου σε αντικείμενα” (object based) από τη γλώσσα.

'''ΠΗΓΕΣ'''
1.[http://el.wikipedia.org/wiki/Fortran]
2.ΣΗΜΙΩΣΕΙΣ ΑΠΟ Σταμάτης Σταματιάδης

Επίσεις να ευχαριστήσω το φιλαράκι μου τον Παναγιώτη, χωρις αυτον δεν 8α υπήρχε το άρθρο.
-----------------------------------------

Δεσμεύομαι να μην ξαναπω Χριστοπαναγίες ή να αποκαλέσω τη μάνα κανενός ιερόδουλη...

εκτός και αν έχω αποδείξεις ότι είναι....


noukist
Νεοφερμένος
Posts: 20
Joined: 2007-12-10
Coins: 6
Dobro member
User is offline
Απ: FORTRAN

Πέδες sorry για τις φατσούλες στο κειμενο αλλα είναι πολυ μεγάλο για να το διορθώσω ολο... Arf

-----------------------------------------

Δεσμεύομαι να μην ξαναπω Χριστοπαναγίες ή να αποκαλέσω τη μάνα κανενός ιερόδουλη...
εκτός και αν έχω αποδείξεις ότι είναι....