Προς το περιεχόμενο

Βοήθεια με fortran


netpumber

Προτεινόμενες αναρτήσεις

Δημοσ.

Καλησπέρα σας παιδιά..

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

 

Λοιπόν:

 

Να γραφεί και εκτελεσθεί ένα πρόγραμμα που διαβάζει απο ένα αρχείο την τιμή της ακέραιας μεταβλητής Ν (Ν<=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 ;

 

Μπορείτε να καταλάβετε τι ζητάει ;

 

Ευχαριστώ!

Δημοσ.

Για δες μια γρήγορη και κομψή λύση :

 

>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.

 

-

Δημοσ.

Σε ευχαριστώ πολύ φίλε μου..

 

Σε αυτή όμως τη περίπτωση με τα λουπς τι μπορώ να κάνω;

 

>k=mod(N,3)
DO i=1,N-k,3
 WRITE(7,'(F5.1)')P(i)
END DO

 

Που να βάλω το k ωστε να γράφονται 3 ανα record ?

 

Σε ευχαριστώ..

Δημοσ.

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, δυστυχώς....

 

-

Δημοσ.

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

 

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

 

>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

Δημοσ.

Κακή σύνταξη, λάθος λογική και αβλεψίες. Φαντάζομαι την ασχετοσύνη των διδασκόντων...

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.

Μόνον με μια εντολή ! Είναι πιο διαισθητικό και πολύ βολικό να σκέπτεσαι πινακοειδώς.

 

Τέλος μαθήματος. :rolleyes:

Έχω γενικά να παρατηρήσω ότι ενώ η fortran είναι κομψότατη και πανίσχυρη, την διδάσκουν άσχετοι και με εντελώς

λάθος τρόπο. Το αποτέλεσμα είναι να μην φαίνονται δυνατότητές της και να καταστρέφεται η υπόληψή της στους χρήστες.

Κι' όχι τίποτε άλλο αλλά σε πολλά είναι απλούστερη και βολικότερη από την C/C++.

 

-

Δημοσ.

Σε ευχαριστώ για ακόμα μια φορά πολύ!

 

Λές

 

Αν δεν βρει όλα τα στοιχεία (εδώ Ν, δηλ. 10) θα δώσει λάθος. (Μπορείς να χειριστείς και αυτό αλλά άστο προς το παρόν.)

 

 

Αυτό μπορείς να το χειριστείς με τις ERR/END= ; ή κάνω λάθος;

 

Πραγματικά σε ευχαριστώ και πάλι..Να σε καλά! :) :)

Δημοσ.

Ένας τρόπος είναι κι' αυτός.

 

Είχες γράψει αρχικά :

>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 θα εκτρέψει την εκτέλεση στην επιθυμητή γραμμή και ο πίνακας θα έχει γεμίσει μέχρι εκείνο το σημείο.

 

Καλή συνέχεια....

-

Αρχειοθετημένο

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

  • Δημιουργία νέου...