ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΤΜΗΜΑ ΜΗΧΑΝΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΒΙΟΜΗΧΑΝΙΑΣ Διάλεξη 4: Δείκτες, συναρτήσεις και διαδικασίες Εαρινό εξάμηνο 2009 ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ι. Σαρρής, τηλ.
Παράδειγμα INTEGER, POINTER::P INTEGER, TARGET ::X=10, Y=0 P=>X Χ=11 Χ=10 Y=P P=>Y P=>20 P=>y+1 P=10 P=y+1 NULLIFY(P) END
Ανάθεση σε δείκτες (συνέχεια)
Δείκτες πινάκων Οι δείκτες μπορούν να χρησιμοποιηθούν και σαν δυναμικά ψευδώνυμα ολόκληρων πινάκων ή τμημάτων τους. REAL, TARGET::A(6,6) REAL, POINTER::cntr(:), row(:) cntr=> A(3:4,3:4); row=A(3:) REAL, TARGET::A(-3:2) REAL, POINTER::row(:) row=>A! a) row (-3:2) row=A(:) ! b) row (1:6) row=A(0::2) ! c) row (1:2)
Δείκτες πινάκων (συνέχεια) Αν ο δείκτης συσχετίζεται με τμήμα ενός πίνακα, το κάτω όριο του είναι 1 και το άνω όριο ίσο με την έκταση του τμήματος, έστω και αν το τμήμα είναι ίσο με τον πίνακα. Αν ο δείκτης σχετίζεται με ολόκληρο πίνακα, τα όρια και οι ενδεικτές του είναι ίδια με του πίνακα. Δεν μπορούμε να χρησιμοποιήσουμε διανυσματικούς ενδεικτές για να προσδιορίσουμε τμήμα πίνακα που σχετίζεται με δείκτη. Ο μόνος τρόπος είναι οι τριάδες. Οι πίνακες δεικτών είναι εξ ορισμού ALLOCATABLE και δεν χρειάζονται DEALLOCATE.
Δείκτες και διαδικασίες Οι δείκτες μπορούν να χρησιμοποιηθούν σαν εικονικές μεταβλητές διαδικασιών ή σαν παράμετροι του ορίσματος της κλήσης τους. Το αποτέλεσμα μιας διαδικασίας τύπου FUNCTION μπορεί να είναι δείκτης Περιορισμοί: 1.Η αντίστοιχη παράμετρος κλήσης πρέπει να είναι επίσης δείκτης του ίδιου τύπου, είδους και τάξης. 2.Ένας εικονικός δείκτης δεν μπορεί να έχει την ιδιότητα INTENT και οι διαδικασίες δεν μπορούν να είναι PURE ή ELEMENTAL. 3.Οι διαδικασίες που χρησιμοποιούν εικονικούς δείκτες ή στόχους πρέπει να έχουν ρητή διεπιφάνεια (θα δούμε γιατί όταν θα μιλήσουμε για τα αντικείμενα). 4.Τέλος, αν μια παράμετρος κλήσης της διαδικασίας είναι δείκτης, ενώ ο αντίστοιχος εικονικός όρος της διαδικασίας δεν είναι, τότε ο εικονικός όρος συσχετίζεται με το στόχο του δείκτη (ο δείκτης απαναφέρεται)
Παράδειγμα 1 PROGRAM pointers IMPLICIT NONE REAL, DIMENSION(0:100)::X REAL, DIMENSION(:), POINTER::P INTEGER:: I X=0.; X(::40)=(/(I,I=1,3)/) CALL nonzero(X,P) PRINT*, P; PRINT*, SIZE(P) CONTAINS SUBROUTINE nonzero(a,b) REAL, DIMENSION(:)::a REAL, DIMENSION(:), POINTER::b INTEGER:: i, j, n n = COUNT(a>1.E-7) ALLOCATE(b(n)) DO j = 1, SIZE(a) IF(a(j)<1.E-7) CYCLE i=i+1 b(i)=a(j) END DO END SUBROUTINE nonzero END PROGRAM pointers
Παράδειγμα 2 PROGRAM pointers IMPLICIT NONE REAL, DIMENSION(0:100)::X REAL, DIMENSION(:), POINTER::P INTEGER:: I, n X=0.; X(::40)=(/(I,I=1,3)/) n = COUNT(X>1.E-7) ALLOCATE(P(n)) CALL nonzero(X,P) PRINT*, P; PRINT*, SIZE(P) CONTAINS SUBROUTINE nonzero(a,b) REAL, DIMENSION(:)::a,b INTEGER:: i, j DO j = 1, SIZE(a) IF(a(j)<1.E-7) CYCLE i=i+1 b(i)=a(j) END DO END SUBROUTINE nonzero END PROGRAM pointers
Παράδειγμα 3 PROGRAM pointers IMPLICIT NONE REAL, DIMENSION(0:100)::X REAL, DIMENSION(:), POINTER::P INTEGER:: I X=0.; X(::40)=(/(I,I=1,3)/) P=> nonzero(X) PRINT*, P; PRINT*, SIZE(P) CONTAINS FUNCTION nonzero(a) REAL, DIMENSION(:)::a REAL, DIMENSION(:), POINTER:: nonzero INTEGER:: n, i, j n = COUNT(a>1.E-7) ALLOCATE(nonzero(n)) DO j = 1, SIZE(a) IF(a(j)<1.E-7) CYCLE i=i+1 nonzero(i)=a(j) END DO END FUNCTION nonzero END PROGRAM pointers
Παράδειγμα 4 PROGRAM pointers IMPLICIT NONE REAL, DIMENSION(0:100)::X REAL, DIMENSION(:), POINTER::P INTEGER:: I INTERFACE FUNCTION nonzero(a) IMPLICIT NONE REAL, DIMENSION(:)::a REAL, DIMENSION(:), POINTER:: nonzero END FUNCTION nonzero END INTERFACE X=0.; X(::40)=(/(I,I=1,3)/) P=> nonzero(X) PRINT*, P; PRINT*, SIZE(P) END PROGRAM pointers FUNCTION nonzero(a) IMPLICIT NONE REAL, DIMENSION(:)::a REAL, DIMENSION(:), POINTER:: nonzero INTEGER:: n, i, j n = COUNT(a>1.E-7) ALLOCATE(nonzero(n)) DO j = 1, SIZE(a) IF(a(j)<1.E-7) CYCLE i=i+1 nonzero(i)=a(j) END DO END FUNCTION nonzero
Εξωτερικές διαδικασίες Ιστορικά οι εξωτερικές διαδικασίες ήταν το μόνο διαθέσιμο είδος διαδικασιών στην Fortran 77 Η χρήση τους στην Fortran 90 περιορίζεται στην αυτόματη επίλυση ολοκληρωμένων υπο-προβλημάτων. Όπως και οι εσωτερικές διαδικασίες είναι και αυτές τύπου SUBROUTINE ή FUNCTION Δεν περιέχονται μέσα στην έκταση CONTAINS…END, αλλά είτε σε ένα άλλο αρχείο πηγαίου κώδικα (με την κατάληξη.F90) ή μετά από την πρόταση END PROGRAM Κάθε εξωτερική διαδικασία μεταγλωττίζεται ξεχωριστά, μπορεί να περιέχει εσωτερικές διαδικασίες και μπορούν να έχουν πολλαπλές εισόδους.
Εξωτερικές διαδικασίες (συνέχεια) Η κυριότερη διαφορά με τα άλλα είδη διαδικασιών είναι ότι οι εξωτερικές διαδικασίες δεν έχουν ρητή διεπιφάνεια. Πρακτικά αυτό σημαίνει ότι: 1.Ο μεταγλωττιστής δεν ξέρει κατά την μεταγλώττιση τον τρόπο κλίσης της διαδικασίας. 2.Ο προγραμματιστής πρέπει να έχει φροντίσει ώστε να καλεί την εξωτερική διαδικασία με τον σωστό τρόπο 3.Υπάρχει η δυνατότητα να δηλώσουμε ρητά την διεπιφάνεια των εξωτερικών διαδικασιών χρησιμοποιώντας την δομή INTERFACE… END INTERFACE, πχ INTERFACE [γενικός ορισμός] [διεπιφάνειες] [MODULE PROCEDURE λίστα διαδικασιών] END INTERFACE Όπου, [διεπιφάνειες] : Η αρχική και η τελική πρόταση μιας ή περισσότερων διαδικασιών μαζί με το τμήμα δηλώσεων της διαδικασίας.
Παραδείγματα INTERFACE REAL FUNCTION f(x,y) REAL, INTENT(IN)::x,y END FUNCTION f SUBROUTINE fa(x,y,z) REAL, INTENT(IN)::x,y REAL, INTENT(OUT)::z END SUBROUTINE fa END INTERFACE PROGRAM Prog_interface IMPLICIT NONE INTERFACE INTEGER ELEMENTAL FUNCTION f(x) INTEGER, INTENT(IN)::x END FUNCTION f END INTERFACE INTEGER::I INTEGER, DIMENTION(5)::a=(/(i,i=1,5)/) PRINT*, f(a) END PROGRAM Prog_interface INTEGER ELEMENTAL FUNCTION f(x) INTEGER, INTENT(IN )::x f=x**2-1 END FUNCTION f
Παρατηρήσεις Αν στο προηγούμενο παράδειγμα ξεχάσουμε την δομή INTERFACE, τότε θα δούμε ότι ο μεταγλωττιστής δεν θα μπορεί να επισημάνει το όνομα της διαδικασίας και θα βγάλει το μήνυμα λάθους: Error: unresolved external Εναλλακτικά μπορούμε να κάνουμε γνωστή την ύπαρξη της διαδικασίας χρησιμοποιώντας την εντολή EXTERNAL, π.χ: INTEGER, EXTERNAL :: f Γενικά, προτείνουμε την χρήση της δομής INTERFACE αφού γνωστοποιεί στο πρόγραμμα που την καλεί την ύπαρξη της εξωτερικής διαδικασίας και ταυτόχρονα διασφαλίζει την αντιστοίχηση των όρων των δύο ορισμάτων
Παρατηρήσεις (συνέχεια) Η χρήση ρητών διεπιφανειών είναι υποχρεωτική μόνο στις εξής περιπτώσεις: 1. Όταν χρησιμοποιούμε εικονικούς όρους που έχουν δηλωθεί ως OPTIONAL. 2.Όταν το αποτέλεσμα μιας FUNCTION είναι πίνακας 3.Όταν το αποτέλεσμα μιας FUNCTION είναι δείκτης 4.Όταν το αποτέλεσμα μιας FUNCTION είναι αλφαριθμητικό με άγνωστο μήκος 5.Όταν χρησιμοποιούμε πίνακα υποθετικής μορφής 6.Όταν η διαδικασία έχει εικονικούς όρους με τις ιδιότητες POINTER ή TARGET. 7.Όταν χρησιμοποιούμε λέξεις κλειδιά στο όρισμα κλήσης μιας διαδικασίας. 8.Όταν η διαδικασία είναι γενική (όταν χρησιμοποιούμε το ίδιο όνομα για την κλήση διαφορετικών διαδικασιών ανάλογα με το είδος των όρων του ορίσματος) 9.Όταν οι διαδικασίες είναι PURE, κλπ
Λίστες Οι δείκτες μας δίνουν τη δυνατότητα δημιουργίας δυναμικών δομών όπως οι συνδεδεμένες λίστες (ουρές, σωρούς, δενδριτικές δομές, κλπ) Οι δομές αυτές μας δίνουν την δυνατότητα της κατά βούλησης αύξησης και μείωσης του μεγέθους τους κατά την διάρκεια της εκτέλεσης. TYPE list INTEGER ::value TYPE (list), POINTER::next ! Αναδρομική δήλωση END TYPE list
Λίστες (συνέχεια) Η περιγραφή του τύπου list είναι αναδρομική Δεν έχουμε δηλώσει πουθενά το μέγεθος της λίστας Η δημιουργία της λίστας μπορεί να χωριστεί σε τέσσερα στάδια 1.Δημιουργείται ο τύπος της λίστας με την αναδρομική δήλωση TYPE list … END TYPE list, και δηλώνονται δύο δείκτες αυτού του τύπου. Αρχικά αποσυσχετίζονται από κάθε πιθανό στόχο. first NULL last NULL
Λίστες (συνέχεια) 2. Για να δημιουργήσουμε το πρώτο στοιχείο της λίστας συσχετίζουμε τους δύο δείκτες μεταξύ τους (last=>first), αποσυσχετίζουμε το επόμενο στοιχείο της λίστας (last%next=>null()) και δίνουμε τιμή (last%value=number) value nextNULL firstlast
Λίστες (συνέχεια) 3. Δημιουργείται κάθε στοιχείο της λίστας (last =>last%next) το επόμενο στοιχείο αποσυσχετίζεται (last%next =>null()) και το τρέχον παίρνει τιμή (last%value=number) value next value nextNULL first last
Λίστες (συνέχεια) value next value next value nextNULL first last 4. Με την εντολή first =>first%next διατρέχουμε την λίστα από την αρχή προς το τέλος της
Παράδειγμα PROGRAM linkedlist IMPLICIT NONE TYPE list INTEGER ::value TYPE (list), POINTER::next END TYPE list TYPE(list), POINTER ::first,last INTEGER:: number, status NULLIFY(first,last) READ*, number IF(number/=0) THEN ALLOCATE(first, STAT=status) IF(status/=0) STOP ‘NOT ENOUGH MEMORY’ last=>first last%next=>NULL() last%value= number reading:DO READ(*,*) number; IF(number==0) EXIT ALLOCATE(last%next, STAT=status) IF(status/=0) STOP ‘NOT ENOUGH MEMORY’ last=>last%next last%next=>NULL() last%value= number END DO reading END IF printing:DO WHILE(ASSOCIATED(first)) PRINT*, first%value first=>first%next END DO printing END PROGRAM linkedlist