Σελίδα 1 από 1

FORTRAN

ΔημοσίευσηΔημοσιεύτηκε: 26 Φεβ 2010, 13:31
από Dimitris
FORTRAN
Ή "η νέα γλώσσα προγραμματισμού"

Ακούω γέλια; Ναι η FORTRAN είναι μια σύγχρονη γλώσσα προγραμματισμού που μαθαίνεται από πολλούς νέες και νέους στο forum μας, αλλά δυστυχώς δε διδάσκεται (τουλάχιστον σωστά). Και επειδή αυτοί που τη διδάσκουν δε ξέρουν από ελεύθερο λογισμικό, οπότε δε μπορούν να γράψουν έναν οδηγό και να τον διαθέσουν ελεύθερο, αποφάσισα να κάνω εγώ αυτή τη δουλειά. (Όχι ότι δε μου κάνει κέφι)

Πολλές από τις πληροφορίες που αναφέρω εδώ βρίσκονται ήδη στους οδηγούς της υπογραφής μου, αλλά τις επαναλαμβάνω για τους τεμπέληδες. Καταρχήν (επειδή έχω κουραστεί να ακούω το logari81 να τονίζει τη διαφορά μεταξύ compiler, IDE, κλπ κλπ) υπάρχει το IDE, Integrated Development Environment, με το οποίο δεν πρόκειται να ασχοληθώ καθόλου. Όπως λέει η λέξη είναι ένα ολοκληρωμένο περιβάλλον ανάπτυξης, τίποτε λιγότερο τίποτε παραπάνω. Δεν είναι compiler, δεν είναι linker. Παράδειγμα: geany.

Compiler: Είναι το πρόγραμμα εκείνο (γραμμής εντολών) που παίρνει τον πηγαίο κώδικα και το μετατρέπει σε δυαδικό εκτελέσιμο. Πάλι τίποτε λιγότερο τίποτε παραπάνω. Στον compiler ΔΕ γράφουμε κώδικα. Παραδείγμα: gfortran. Εγκατάσταση:
sudo apt-get install gfortran

Linker: Είναι το πρόγραμμα εκείνο (γραμμής εντολών) που "συνδέει" όλα τα δυαδικά αρχεία σε ένα εκτελέσιμο. Πάλι τίποτε λιγότερο τίποτε παραπάνω. Πολλές φορές ο compiler καλεί, ξανατονίζω καλεί, τον linker. Ο compiler ΔΕΝ κάνει linking. Kαλεί απλώς το linker. Παράδειγμα: ld.

Τώρα που ξεκαθαρίσαμε τις βασικές έννοιες μπορούμε να προχωρήσουμε με τη FORTRAN. Όπως είπαμε FORTRAN είναι μια γλώσσα προγραμματισμού. Άλλα έχει πολλά πρότυπα, standards, όπως fortran 66, fortran 77, fortran 90/95, fortran 2003.Όταν γράφεις ένα πρόγραμμα αποφασίζεις σε ποιο πρότυπο θα το γράψεις, ή άλλοι το έχουν αποφασίσει πριν από σένα. Σε γενικές γραμμές είναι backwards compatible. Δε θα μπω σε λεπτομέρειες. Το fortran 66 είναι απαρχαιωμένο, μην το χρησιμοποιείτε. Το fortran 77 είναι μεν παλιό αλλά υπάρχει πολύ "legacy" κώδικας εκεί έξω που είναι γραμμένο σε αυτό. Αν ξεκινάτε κάτι καινούριο, τότε fortran 90/95. To fortran 2003 είναι ακόμη καινούριο, μη δοκιμασμένο και είναι πολύ πιθανό ο compiler gfortran να μην έχει όλες τις δυνατότητες που προδιαγράφονται από το πρότυπο. Ο gfortran υποστηρίζει τα πρότυπα 77 και 90/95 (για τα άλλα δεν είμαι σίγουρος). Ο g77 μόνο το 77 (όπως μαντεύετε και από το όνομα)

Αυτά και με τα πρότυπα. Μία άλλη σημαντική παρατήρηση είναι το format. Στο fortran 77 επειδή είχαν ακόμη κάρτες διάτριτες (τώρα μπορείτε να δείτε σε μουσεία τέτοιες) το πρότυπο προέβλεπε οι πρώτες 6 στήλες να είναι κενές και επιτρέπεται να γράφεις μέχρι την εβδομικοστή κάτι στήλη. Αυτό ονομάζεται fixed format, ενώ το ελεύθερο ονομάζεται free format. O compiler gfortran υποστηρίζει και τα δύο ανάλογα με την κατάληξη του αρχείου ή το command line argument: -ffree-form -ffixed-form και άλλα που μπορούν να βρεθούν εδώ http://linux.die.net/man/1/gfortran

Για να σκεφτούμε, τι άλλο χρειάζεται; Α ναι, ένα παράδειγμα. Γράφουμε στο αρχείο foo.f:
print*, "hello world"
end

Ανοίγουμε το τερματικό και προσπαθούμε να το κάνουμε compile με:
gfortran foo.f
σφάλμα

ξαναπροσπαθούμε:
gfortran -ffree-form foo.f
οκ

το μετονομάζουμε το αρχείο σε foo.f90 και ξαναπροσπαθούμε:
gfortran foo.f90
οκ

Τώρα στο αρχείο foo.f προσθέτουμε 6 κενά σε κάθε γραμμή:
print *, "hello, world"
end
και ξαναπροσπαθούμε
gfortran foo.f
οκ

You got the point? Ωραία αλλάζουμε κεφάλαιο. (Με προσοχή τις σελίδες γιατί είναι παλιές και ευαίσθητες)

Πριν αλλάξουμε κεφάλαιο (με προσοχή τις σελίδες είπαμε) ας δούμε τη διαφορά compiling και linking. Mέχρι στιγμής λέγαμε στον compiler να κάνει τη όλη δουλειά μόνος του, δηλαδή και το compile και να καλέσει το linker (αν έχετε ανοιχτό το top σε άλλο τερματικό θα δείτε που τρέχει στιγμιαία η εντολή ld). Aν όμως εκτελέσουμε την εντολή
gfortran -c foo.f
Εδώ γίνεται μόνο compile, δημιουργείται το *.ο αρχείο του πηγαίου κώδικα.

Τώρα με
gfortran -o a.out foo.o
λέμε στον compiler να συνδέσει το εκτελέσιμο a.out με το δυαδικό foo.o.

Αυτά. Happy crunching!

Re: FORTRAN

ΔημοσίευσηΔημοσιεύτηκε: 28 Μάιος 2011, 00:24
από Dimitris
Θα ήθελα να περιγράψω πολύ επιγραμματικά μερικές από τις αντικειμενοστραφείς ιδιότητες της Fortran 90/95, οι οποίες δεν είναι και τόσο γνωστές.

Καταρχήν, η χρήση modules κάνει τη διεπιφάνεια συναρτήσεων και υπορουτινών πιο καθαρή, καθώς και τη χρήση common blocks περιττή. Σε ένα αρχείο foofile.f90 γράφουμε τα εξής:

Κώδικας: Επιλογή όλων
module foo

integer :: intfoo

contains

subroutine subfoo
write(*,*) 'do something'
end subroutine

end module


και στο main.f90 έχουμε:
Κώδικας: Επιλογή όλων
program main

use foo

intfoo = 3
write(*,*) intfoo

call subfoo()

end program main

H μεταβλητή intfoo είναι μια ολική μεταβλητή του module (όχι και πολύ καθαρός τρόπος προγραμματισμου). Όπως βλέπεις όλες οι μεταβλητές και υπορουτινες του module foo είναι προσβασιμες από το κυρίως πρόγραμμα αν γράψουμε use foo. Βέβαια μέχρι στιγμής δεν κάναμε τίποτε το αντικειμενοστραφές. Τα αντικειμένα στη fortran ορίζονται ως νέοι τύποι μεταβλητών με τη βοήθεια του type.

Για παράδειγμα:

Κώδικας: Επιλογή όλων
type rectangle
real :: a, b
end type rectangle


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

Ας δημιουργήσουμε ένα instance του αντικειμένου rectangle:

Κώδικας: Επιλογή όλων
type(rectangle) :: z

Τώρα μπορούμε να θέσουμε (set) τις ιδιότητες του αντικειμένου rectangle ως εξής:

Κώδικας: Επιλογή όλων
z%a = 1.0
z%b = 2.0


Καλή προγραμματιστική τεχνική είναι να πακετάρουμε το set σε μία "μέθοδο" του rectangle:

Κώδικας: Επιλογή όλων
subroutine set_rectangle(zloc, aloc, bloc) ! τις ονόμασα ?loc για να δείξω ότι είναι τοπικές μεταβλητές της set_rectangle

type(rectangle) :: zloc
real, intent(in) :: aloc, bloc

zloc%a = aloc
zloc%b = bloc

end

call set_rectangle(z, 1.0, 2.0)


ή ως function

Κώδικας: Επιλογή όλων
function set_rectangle(aloc, bloc)

type(rectangle) :: set_rectangle
real, intent(in) :: aloc, bloc

set_rectangle%a = aloc
set_rectangle%b = bloc

end

z = set_rectangle(1.0, 2.0)


Αντίστοιχα μπορούμε να υλοποιήσουμε τη get method ενός αντικειμένου, η οποία επιστρέφει μια ιδιότητα του αντικειμένου. Θα μπορούσαμε δηλαδή να έχουμε get_a, get_b

Κώδικας: Επιλογή όλων
print *, get_a(z) ! θα πρέπει να τυπώνει 1.0
print *, get_b(z) ! θα πρέπει να τυπώνει 2.0


Αυτό είναι ισοδύναμο με
print *, z%a
αλλά για πιο πολύπλοκα αντικείμενα ίσως είναι καλό να έχεις τις δικές τους συναρτήσεις ή μια γενική get_prop(z, 'prop')

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

Κώδικας: Επιλογή όλων
function area(x)
type(rectangle) :: x
real :: area
area = x%a * x%b
end


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

Η πολυμορφία σημαίνει ότι υλοποιούμε και το αντικείμενο circle, με τις set/get kai με τη μέθοδο area. Τώρα θέλουμε να γενικεύσουμε το πρόγραμμά μας έχοντας σχήματα, δηλαδή ένα νέο type :: shape που ανάλογα με το είδος του, θα επιλέγει ποια μέθοδο να καλέσει για αντίστοιχο σχήμα.

Η υπερφόρτωση τελεστών σημαίνει ότι μπορουμε να γράφουμε
z = x + y
όπου x, y, z είναι type :: rectangle και έχουμε ορίσει μια συνάρτηση που αντιστοιχεί στην πρόσθεση παραλληλογράμμων. Παράλληλα μπορούμε να γράψουμε
z = a*x, όπου x,z είναι type::rectangle ενώ a είναι integer ή real.

Τέλος, κληρονομικότητα είναι ίσως το αντίστοιχο της πολυμορφίας. Εχουμε δηλαδή ένα γενικό αντικείμενο, πχ shape, με τις ιδιότητες perimeter, area (προσοχή τώρα είναι ιδιότητες όχι μέθοδοι) και θέλουμε το ειδικό αντικείμενο rectangle να "κληρονομήσει" αυτές τις ιδιοτητες.

Και για να κεντρίσω το ενδιαφέρον με ένα παράδειγμα, θα μπορούσε κανείς να γράψει

Κώδικας: Επιλογή όλων
multi_stage = initialize()
do i = 1, 6
  multi_stage = multi_stage + (stator + rotor)
end do

call simulate(multi_stage)


Αυτό προϋποθέτει ότι έχουμε υπερφορτώσει τον τελεστή της πρόσθεσης ώστε να μπορεί να εφαρμόσει μια συναρτηση στα αντικείμενα stator και rotor, η οποια θα επιστρέφει ένα αντικείμενο single_stage, και ότι έχουμε υπερφορτώσει τον τελεστή της πρόσθεσης ώστε να μπορεί να εφαρμόσει μια συναρτηση στα αντικείμενα single_stage kai multi_stage, η οποία επιστρέφει ένα αντικείμενο multi_stage. Έπειτα απλά καλούμε τη μέθοδο του αντικειμένου multi_stage. Δε φαίνεται πολύ πιο ωραίος ο παραπάνω κώδικας από τον κλασσικό fortran κώδικα;