netpumber Δημοσ. 30 Νοεμβρίου 2010 Δημοσ. 30 Νοεμβρίου 2010 Καλησπέρα σας παιδιά.. Θέλω αν μπορείτε να με βοηθήσετε σε ένα πρόγραμμα το οποίο έτσι όπως είναι διατυπομένο δεν μπορώ να το καταλάβω. Λοιπόν: Να γραφεί και εκτελεσθεί ένα πρόγραμμα που διαβάζει απο ένα αρχείο την τιμή της ακέραιας μεταβλητής Ν (Ν<=20), τα Ν στοιχεία ενός μονοδιάστατου πίνακα Α και επίσης τα Ν στοιχεία ενός άλλου μονοδιάστατου πίνακα Β (πρώτα τα στοιχειά του Α και μετα τα στοιχειά του Β). Στη συνέχεια τροποποιούνται τα Ν στοιχεία του μονοδιάστατου πίνακα Α ως εξής: Α1 = Α1 + Β2 , Α2 = Α2 + Β2 , ΑΝ-1 = ΑΝ-1 + ΒΝ , ΑΝ = ΑΝ Τέλος οι πίνακες Α και Β γράφονται σ' ενα αρχείο με τους ανάλογους τύπους. Πρώτα ο πίνακας Α με δυο τιμές ανα record και τρια δεκαδικά ψηφία και μετα ο πίνακας Β με τρεις τιμές ανα record και πεντε δεκαδικα ψηφία. Το αρχείο με την τιμή της μεταβλητής Ν και τα στοιχεία των πινάκων Α και Β είναι: 10 9.1 4.2 -4.4 8.3 -6.1 4.3 7.2 8.4 9.7 5.9 2.5 3.1 4.4 -1.3 7.7 8.2 -6.4 5.3 -0.8 4.1 Με αυτές τις τιμές θα γεμίσω το αρχείο και τους δυο πίνακες ? Επίσης πώς μπορείς να γράψεις 3 τιμές ανα record ; Μπορείτε να καταλάβετε τι ζητάει ; Ευχαριστώ!
V.I.Smirnov Δημοσ. 30 Νοεμβρίου 2010 Δημοσ. 30 Νοεμβρίου 2010 Για δες μια γρήγορη και κομψή λύση : >program ergasia implicit none integer::N,i,j,k real,allocatable::A(,B( open(unit=10,file="data_in.txt") read (10,*) N allocate(A(N),B(N)) read (10,*) A read (10,*) B close(10) A(:N-1) = A(:N-1) + B(2:N) open(unit=20,file="data_out.txt") k=mod(N,2) write (20,'(2f10.3)') (A(i:i+1), i=1,N-k,2) write (20,'(<k>(f10.3))') (A(j:N), j=i,N) k=mod(N,3) write (20,'(3f10.5)') (B(i:i+3), i=1,N-k,3) write (20,'(<k>(f10.5))') (B(j:N), j=i,N) close(20) end program Για την δεδομένη μορφή του αρχείου εισόδου, το διάβασμα των τιμών των πινάκων γίνεται χωρίς καν βρόγχους. Η απόδοση τιμών που ζητά η εκφώνηση γίνεται απλούστατα με τη εντολή Α(:N-1) = A(:N-1) + B(2:N) επίσης χωρίς χρήση βρόχων. Το πλήθος στοιχείων 10 δεν διαιρείται ακριβώς με το 3, άρα κατά την αποθήκευση του B κάποια γραμμή θα έχει λιγότερα από 3. Για να γραφεί γενικά το πρόγραμμα κάνουμε ένα τέχνασμα. Βρίσκουμε το υπόλοιπο της διαίρεσης του N (=10) με το 3 με την εντολή mod. Έστω ότι είναι k. Γράφουμε ανά τριάδες τα στοιχεία του B από 1 έως Ν-k και ότι περισσεύει (λιγότερο από 3) γράφεται χωριστά, από εκεί που μείναμε έως το N. Για τον Α, το 2 διαιρεί ακριβώς το 10 και δεν χρειάζεται κάτι τέτοιο αλλά το κάνω το ίδιο και εκεί, έτσι, για να είναι κι' αυτό γενικά γραμμένο. Τις υπόλοιπες εντολές (ορισμός πινάκων, εκτύπωση δεκαδικών κλπ) θα τις δεις στο help. -
netpumber Δημοσ. 30 Νοεμβρίου 2010 Μέλος Δημοσ. 30 Νοεμβρίου 2010 Σε ευχαριστώ πολύ φίλε μου.. Σε αυτή όμως τη περίπτωση με τα λουπς τι μπορώ να κάνω; >k=mod(N,3) DO i=1,N-k,3 WRITE(7,'(F5.1)')P(i) END DO Που να βάλω το k ωστε να γράφονται 3 ανα record ? Σε ευχαριστώ..
V.I.Smirnov Δημοσ. 30 Νοεμβρίου 2010 Δημοσ. 30 Νοεμβρίου 2010 Eίδα ότι ρώτησες και αλλού αλλά σου έριξαν άκυρο... Πολύ απλά είναι τα πράγματα.Διαβασε προσεκτικά τι γράφω. Η write παίρνει δυο παραμέτρους-αριθμούς. Η πρώτη δείχνει μια εικονική κονσόλα ή αρχείο (στην fortran λέγονται unit) όπου γράφονται τα δεδομένα. Εσύ εδώ έχεις δώσεις 7 και προφανώς έχεις ήδη ανοίξει με την open ένα αρχείο για να γράψεις εκεί και του αντιστοιχείς τον αριθμό (Id) 7. Βάζοντας αστερίσκο , δηλ. write(*,*) γράφει στην εξ ορισμού unit που είναι η οθόνη και αν θυμάμαι καλά έχει αριθμό 6. Γράφεις λοιπόν σ' αυτό με την write(7, Η δεύτερη παράμετρος αντιστοιχεί σε μια εντολή format που δείχνει την μορφοποίηση της γραφής (δηλ. την μορφή με την οποία γράφονται τα δεδομένα). Π.χ. αν έχεις WRITE(7,100) data 100 format(f5.1) θα γραφεί η μεταβλητή data στο αρχείο 7 σύμφωνα με την μορφοποίηση που δείχνει η εντολή format 100. Eναλλακτικά, μπορείς αντί να χρησιμοποιήσεις την format, να βάλεις την μορφοποίηση μέσα στην ίδια την write. Δηλ. να γράψεις WRITE(7,'(f5.1)') και θα είναι το ίδιο όπως ακριβώς και με τη format παραπάνω. Aυτό βολεύει όταν η μορφοποίηση είναι απλή-μικρή. Αν βάλεις τον αστερίσκο, δηλ. WRITE(7,*), θα γράψει χρησιμοποιώντας την εξ ορισμού μορφοποίηση που γενικά δεν θα είναι αυτό που θέλεις να δεις. Εν προκειμένω με το WRITE(7,'(f5.1)') P(i) θα γράφει μόνον ένα στοιχείο και θα αλλάζει γραμμή. Στην περίπτωση με το loop απλώς βάζεις μια μορφοποίηση για να γράφει σε κάθε write 3 στοιχεία κάθε φορά. (Το k=mod(N,3) είναι για να γραφούν τα όποια στοιχεία περισσεύουν κι' όχι για να γραφούν οι τριάδες. ) Το f5.1 δείχνει εκτύπωση ενός πραγματικού με 5 ψηφία από τα οποία το 1 είναι μετά την υποδιαστολή. Ε, το 3f5.1 το κάνει αυτό για 3 πραγματικούς. Και για m πραγματικούς γράφεις <m>f5.1 Να, έτσι > do i=1,N-k,3 write(7,'(3f5.1)') P(i), P(i+1), P(i+3) end do ή με συμβολισμό πινάκων >do i=1,N-k,3 write(7,'(3f5.1)') P(i:i+2) end do Aκόμα πιο απλά, με έμμεσο do στην ίδια την write το παραπάνω γράφεται σε μια μόνο γραμμή : >write(7,'(3f5.1)') (P(i:i+2), i=1,N-k,3) Με κάποιο από τα παραπάνω γράφεις όλες τις τριάδες. Aν έχουν περισσέψει στοιχεία (λιγότερο από 3 πάντως) θα είναι τα τελευταία k=mod(N,3) και πρέπει να γραφούν χωριστά. Ξεκινάς από εκεί που έφτασες μέχρι τώρα δηλ. από το N-k+1 και γράφεις και τα υπόλοιπα k στοιχεία. Πάλι με ένα έμεσο do ξεμπεδεύεις αμέσως >write(7,'(<k>f5.1)') (P(i:N), i=N-k+1,N) Ή με κλασσικό do (άσκοπα και χειρότερα) : >do i=N-k+1,N write(7,'(<k>f5.1)') P(i) end do Τώρα αυτό που έγραψα στο post #2 πρέπει να σου είναι κατανοητό. Aπ' ότι καταλαβαίνω, εξακολουθούν να μην διδάσκουν σωστά την fortran, δυστυχώς.... -
netpumber Δημοσ. 30 Νοεμβρίου 2010 Μέλος Δημοσ. 30 Νοεμβρίου 2010 Σε ευχαριστώ πραγματικά πάρα πολύ φίλε μου.. Η εξήγησή σου μέτρησε.. Έψαχνα χθές μεχρι τις 3 το πρωί να βρώ λύση..στο ιντερνετ.. Αν σε ήξερα προσωπικά θα σ έλεγα να βγούμε να σε κεράσω κανενα ποτό.. Έχεις δίκιο.. πιθανότατα δεν ξέρουν να την διδάσκουν.. Αλλα μιας που σε βρήκα για δες λίγο αυτόν τον κώδικα που έχω γράψει.. Είναι άσχετος με τα παραπάνω.. >PROGRAM METEOR CALL X_MESOS(x_m) WRITE(*,*)'X Mesos:' WRITE (*,*)x_m x_m = x_m CALL Y_MESOS(y_m) WRITE(*,*)'Y Mesos:' WRITE (*,*)y_m y_m = y_m CALL FIND_B_AR(x_m,y_m,sumAr) WRITE(*,*)'ARITHMITIS' WRITE(*,*)sumAr STOP END SUBROUTINE X_MESOS(x_m) !X meso OPEN(6,FILE="x.txt") x_meso = 0 sum = 0 DO i=1,10 READ(6,*,END=1)x sum = sum + x 1 END DO x_meso = sum / 10 x_m = x_meso RETURN END SUBROUTINE X_MESOS SUBROUTINE Y_MESOS(y_m) !Y meso OPEN(7,FILE="y.txt") y_meso = 0 sum = 0 DO i=1,10 READ(7,*,END=2)y sum = sum + y 2 END DO y_meso = sum / 10 y_m = y_meso RETURN END SUBROUTINE Y_MESOS SUBROUTINE FIND_B_AR(x_m,y_m,sumAr) !Finding b Arithmiti OPEN(6,FILE="x.txt") OPEN(7,FILE="y.txt") xm = x_m ym = y_m sumAr = 0 DO i=1,10 READ(6,*,END=3)x READ(7,*,END=3)y gin = (x - xm)*(y - ym) sumAr = sumAr + gin 3 END DO RETURN END SUBROUTINE FIND_B_AR Αυτό που καταλαβαίνω είναι οτι δεν μπορεί να περάσει στην DO της διαδικασίας FIND_B_AR τις τιμές του χ και του ψ μέσου..Αν το εκτελέσεις θα σου επιστρέψει για αριθμητή 0.000
V.I.Smirnov Δημοσ. 1 Δεκεμβρίου 2010 Δημοσ. 1 Δεκεμβρίου 2010 Κακή σύνταξη, λάθος λογική και αβλεψίες. Φαντάζομαι την ασχετοσύνη των διδασκόντων... Kαταρχήν, οι τιμές μια χαρά περνάνε στην find_B_ar. Aλλού είναι το σφάλμα σου. Δες μια ΣΩΣΤΗ έκδοση αυτού που έγραψες, διάβασε τα σχόλιά μου και θα καταλάβεις. (Με βρήκες να έχω όρεξη ) >program meteor implicit none real:_m,y_m,sumAr call find_X_mesos(x_m) write (*,*) "mesos : ", x_m call find_Y_mesos(y_m) write (*,*) "mesos : ", y_m call find_B_ar(x_m,y_m,sumAr) write(*,*) "arithmitis : ",sumAr pause "Press Enter to continue..." end program subroutine find_X_mesos(x_m) implicit none integer,parameter::N=10 integer::i real::A(N),x_m open(10,file="x.txt") read(10,*) A close(10) x_m=sum(A)/N end subroutine subroutine find_Y_mesos(y_m) implicit none integer,parameter::N=10 integer::i real::A(N),y_m open(20,file="y.txt") read(20,*) A close(20) y_m=sum(A)/N end subroutine subroutine find_B_ar(xm,ym,sumAr) implicit none integer::i,Ν real:,y,g_in, xm,ym, gin,sumAr open(10,file="x.txt") open(20,file="y.txt") Ν=10 ; sumAr=0. do i=1,N read(10,*) x read(20,*) y gin = (x - xm)*(y - ym) sumAr = sumAr + gin end do close(10) close(20) end subroutine 1) Το return δεν χρειάζεται στο τέλος κάθε υπορουτίνας. Το end subroutine ή σκέτο end αρκεί. Το return μπορεί να τεθεί για να τελειώσει η εκτέλεσή της πρόοωρα και να επιστρέψει νωρίτερα. Το stop είναι για να διακόπτει μόνιμα (και με το ζόρι) την εκτέλεση του προγράμματος σε οποιοδήποτε σημείο. Δεν έχει νόημα εκεί που το έβαλες διότι στο end το πρόγραμμα θα τερματιστεί ούτως ή άλλως. Επιπλέον, αν θέλεις να σταματήσεις προσωρινά την εκτέλεση για να δεις π.χ. κάποια αποτελέσματα μπορείς να γράψεις pause "Press Εnter to continue..." 2) Στην fortran υπάρχουν ήδη κάποιες μεταβλητές εξ ορισμού. Ειδικότερα, όσες ξεκινάνε από i-ο είναι ακέραιες και οι υπόλοιπες είναι real. Eπειδή αυτή η προεπιλογή συχνά προξενεί λάθη πρέπει να δηλώνονται ανεξαιρέτως όλες οι μεταβλητές που θα χρησιμοποιηθούν. Αυτό κάνει η implicit none. Δείχνει ότι δεν θα χρησιμοποιηθεί καμιά αδήλωτη μεταβλητή. 3) Είναι καλό αν μπορείς να έχεις τα δεδομένα σε ένα πίνακα και να τα επεξεργάζεσαι από εκεί. Γράφω λοιπόν με αυτή την λογική. Στις υπορουτίνες find_X_mesos, find_Υ_mesos ορίζεται ένας πίνακας, έστω Α, όπου θα αποθηκευτούν προσωρινά τα δεδομένα. Ανοίγεται το αρχείο και διαβάζονται. Για να διαβάσεις τα στοιχεία του πίνακα ΔΕΝ χρειάζεται καν βρόγχος. Yπό την προϋπόθεση ότι υπάρχουν όλα τα ζητούμενα στοιχεία, αρκεί ένα απλό read(10,*) A (όπου εδώ το 10 δείχνει το unit που άνοιξες με την open) Αν δεν βρει όλα τα στοιχεία (εδώ Ν, δηλ. 10) θα δώσει λάθος. (Μπορείς να χειριστείς και αυτό αλλά άστο προς το παρόν.) Με τα δεδομένα στον πίνακα Α, το άθροισμά τους βρίσκεται αμέσως με την εντολή sum(A). Διαιρείς και με το Ν, δηλ. sum(A)/N και καθάρισες. Χωρίς καν do. Ο πίνακας Α πρέπει και αυτός να δηλωθεί. Έχεις 10 θέσεις. Για να αλλάζεις εύκολα το μέγεθός του τον ορίζεις ως real::A(N) Eπειδή και το Ν πρέπει να έχει ήδη οριστεί για να χρησιμοποιηθεί, ακολουθείται μια πιο ειδική σύνταξη. Ορίζεις το Ν με την εντολή integer,parameter::N=10 όπου η λέξη parameter δείχνει ότι το Ν είναι μια σταθερά. Αλλιώς δεν θα μπορείς να βάλεις το Ν άμεσα στον ορισμό του Α. 4) Τα αρχεία που άνοιξες με την open είναι καλό να τα κλείνεις όταν τελειώσεις. Οι ίδιοι αριθμοί αρχείων μπορούν να επαναχρησιμοποιηθούν. Δηλ. μπορείς να έχεις open(10,file="x.txt") στην μια υπορουτίνα και open(10,file="y.txt") στην άλλη ή στην ίδια αρκεί να έχεις κλείσει το αρχείο της πρώτης open. Kαι παρεμπιπτόντως, εδώ υπάρχει σφάλμα σε αυτά που έγραψες. Έχεις ανοίξει τα αρχεία στις δυο υπορουτίνες σου και διάβασες τα x,y. Αλλά δεν τα έκλεισες ! Στην υπορουτίνα find_B_ar τα ξανανοίγεις ενώ δεν έχει νόημα αφού είναι ήδη ανοιχτά. Ο υπολογιστής ξεκινά να διαβάσει από εκεί που έχει μείνει νωρίτερα. Και επειδή ήδη είχε φτάσει στο τέλος δεν έχει τώρα να διαβάσει τίποτε άλλο. Κοντολογίς δεν διαβάζει τα x,y όπως περιμένεις και γι' αυτό δίνει 0. Λύση : - ή κλείνεις τα αρχεία με την close στις προηγούμενες ρουτίνες που τα άνοιξες. - ή βάζεις τον υπολογιστή να επιστρέψει στην αρχή του ανοιχτού αρχείου με την rewind. Π.χ. στην find_B_ar σβήσε τα δυο open που είναι άχρηστα και γράψε rewind(6) ; rewind(7) 5) Οι μεταβλητές σε κάθε υπορουτίνα είναι τοπικές. Όταν στο κύριο πρόγραμμα έχεις find_X_mesos(x_m), στην υπορουτίνα μπορείς να έχεις πχ. subroutine find_X_mesos(opio_Name_thelo).... Για το κύριο πρόγραμμα δεν έχει σημασία πώς ονομάζεις στην υπορουτίνα τα ορίσματα. 6) Στην ίδια γραμμή μπορείς να έχεις πολλές εντολές αρκεί να τις χωρίζεις με ; Π.χ. N=10 ; M=20 ; K=30 Aυτό λέγεται ελεύθερη σύνταξη. (Μιλάμε πάντα για fortran 90/95/2003, έτσι ; στην παλαιά f77 πολλά από τα παραπάνω δεν ισχύουν.) Eπιλέον, δεν υπάρχει διαφορά πεζών-κεφαλαίων (εκτός αν ρυθμίσεις τον compiler). 7) Κανονικά οι δυο ρουτίνες find_Χ_mesos και find_Y_mesos μπορούν να αντικατασταθούν από μία, αλλά ας το αφήσω έτσι... 8) Η υπορουτίνα find_B_ar(xm,ym,sumAr) που υπολογίζεται ο αριθμητής, μπορεί να γραφεί πολύ πιο κομψά ως >subroutine find_B_ar(xm,ym,sumAr) implicit none integer,parameter::N=10 integer::i real:,y, A(N),B(N), xm,ym, sumAr open(10,file="x.txt") open(20,file="y.txt") read(10,*) A read(20,*) B sumAr = sum((A-xm)*(B-ym)) close(10) close(20) end subroutine όπου το μασούρι > sumAr=0. do i=1,N read(10,*) x read(20,*) y gin = (x - xm)*(y - ym) sumAr = sumAr + gin end do αντικαθίσταται με την εντολή : >sumAr = sum((A-xm)*(B-ym)) Η έκφραση αυτή κάνει πράξεις πινάκων : - παίρνει τα στοιχεία του πίνακα Α και αφαιρεί από καθένα το xm - όμοια για τον Β και το ym - πολλαπλασιάζει τους πίνακες που προκύπτουν στοιχείο με στοιχείο - βρίσκει το άθροισμα με την εντολή sum. Μόνον με μια εντολή ! Είναι πιο διαισθητικό και πολύ βολικό να σκέπτεσαι πινακοειδώς. Τέλος μαθήματος. Έχω γενικά να παρατηρήσω ότι ενώ η fortran είναι κομψότατη και πανίσχυρη, την διδάσκουν άσχετοι και με εντελώς λάθος τρόπο. Το αποτέλεσμα είναι να μην φαίνονται δυνατότητές της και να καταστρέφεται η υπόληψή της στους χρήστες. Κι' όχι τίποτε άλλο αλλά σε πολλά είναι απλούστερη και βολικότερη από την C/C++. -
netpumber Δημοσ. 1 Δεκεμβρίου 2010 Μέλος Δημοσ. 1 Δεκεμβρίου 2010 Σε ευχαριστώ για ακόμα μια φορά πολύ! Λές Αν δεν βρει όλα τα στοιχεία (εδώ Ν, δηλ. 10) θα δώσει λάθος. (Μπορείς να χειριστείς και αυτό αλλά άστο προς το παρόν.) Αυτό μπορείς να το χειριστείς με τις ERR/END= ; ή κάνω λάθος; Πραγματικά σε ευχαριστώ και πάλι..Να σε καλά! :)
V.I.Smirnov Δημοσ. 1 Δεκεμβρίου 2010 Δημοσ. 1 Δεκεμβρίου 2010 Ένας τρόπος είναι κι' αυτός. Είχες γράψει αρχικά : >DO i=1,10 READ(6,*,END=3)x READ(7,*,END=3)y gin = (x - xm)*(y - ym) sumAr = sumAr + gin 3 END DO Το end στην read έχει αποτέλεσμα όταν φτάσει στο τέλος του αρχείου, να εκτρέπει την εκτέλεση στην γραμμή 3 ΕND DO. Αλλά αυτό δεν είναι σωστό διότι έτσι ο βρόγχος συνεχίζεται άσκοπα μέχρι να τελειώσει. Π.χ. αν είχες do i=1,1000 και υπάρχουν 5 στοιχεία γιατί να συνεχίζει άσκοπα 995 φορές ; Γράψτο ως >mpla mpla end do 3 i=0 Έτσι μόλις φτάσει στο τέλος γίνεται εκτροπή έξω από τον βρόγχο και δεν συνεχίζει να το εκτελεί άσκοπα. Η err κάνει το ίδιο αλλά η εκτροπή γίνεται αν βρει λάθος (π.χ. εναν χαρακτήρα ενώ περιμένει αριθμό ή λιγότερους αριθμούς από τους αναμενόμενους). Σε μια read μπορούν να υπάρχουν ταυτόχρονα οι err και end. Στην περίπτωση του πίνακα, αν τα στοιχεία είναι λιγότερα ή κάποιο είναι λάθος (π.χ. χαρακτήρας αντί αριθμός) η err θα εκτρέψει την εκτέλεση στην επιθυμητή γραμμή και ο πίνακας θα έχει γεμίσει μέχρι εκείνο το σημείο. Καλή συνέχεια.... -
Προτεινόμενες αναρτήσεις
Αρχειοθετημένο
Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.