Fortran 95 -ohjelmointikieli: Harjoituksia Sebastian von Alfthan E-mail: sebastian.von.alfthan@@csc.fi Juha Haataja E-mail: juha.haataja@csc.fi Jarmo Pirhonen E-mail: jarmo.pirhonen@csc.fi CSC Tieteellinen laskenta Oy 10. 05. 2007
Käytännön järjestelyt Koneessa corona.csc.fi on tunnukset koulu1... koulu20. Salasana on sole74ct Hakemistosta $DOC/f90 löytyy kurssin oheismateriaalia. Hakemiston $DOC/f90/esim/ esimerkkiohjelmat kannattaa kopioida itselleen. Kurssimateriaalina on lisäksi CSC:n ympäristön pikaohje ja Emacs-pikaopas. Kurssi seuraa läheisesti CSC:n Fortran 95/2003 -opasta. Jos haluat käyttää Emacsin f90-moodia, kopioi tiedosto $DOC/f90/emacs työhakemistoosi nimellä.emacs. Suluissa oleva harjoituksen numero viittaa tehtävän numerointiin Fortran 95 -oppaassa. Suositellut Fortran-tehtävät harjoituksiin Harjoitus A (perusteet): tehtävät 1, 2, 3, 4 Harjoitus B (proseduurit): 5, 6 Harjoitus C (taulukot): 7, 8, 9, 10 Harjoitus D (moduulit): 11, 12
Yksinkertainen käännös Esimerkkiohjelma testi.f90: PROGRAM Testi IMPLICIT NONE INTEGER, PARAMETER :: tulos = 57! Yksinkertainen tulostuskäsky... PRINT *, Hei maailma! PRINT *, Tulos on, tulos END PROGRAM Testi Kopiointi omaan kotihakemistoon, käännös ja ajo: cp $DOC/f90/esim/testi.f90 f95 testi.f90 -o testi./testi Hei maailma! Tulos on 57
Harjoitus 1: koodin syöttömuoto 1. (4.1) Mitä seuraava (vapaan formaatin) koodirivi tarkoittaa? A = 0.0 ; B = 370! Alustus ; C = 17.0 ; D = 33.0 2. (4.2) Onko seuraava syntaktisesti oikeaa Fortran 90 -koodia? Y = SIN(MAX(X1,X2)) * EXP(-COS(X3)**I) - TAN(AT& & AN(X4)) 3. (4.3) Onko seuraava määrittely oikein? REAL :: real; REAL :: price 4. (4.5) Onko seuraavat Fortranin lauseet kirjoitettu oikein? merkkijono = Aamun virkku, & illan torkku. x = 0.4-6 vastaus = Tosi & epätosi ala-raja = 0.0005E10 y = E6
Harjoitus 2: muuttujien määrittely 1. (5.1) Ovatko seuraavat määrittelyt laillista Fortrania: DOUBLE :: x CHARACTER(LEN=*), PARAMETER :: "Nimi" REAL :: pi = 22/7 REAL :: x = 2., y = -3 REAL :: pii = 22.0/7.0 REAL x = 1.0 2. (5.2) Määrittele vakio k, jonka arvo on 0.75. 3. (5.5) Tutki, millaisia kokonais- ja reaalilukuarvoja käyttämäsi Fortran-kääntäjä osaa käsitellä. Ohje: tutki aliohjelmakutsujen SELECTED_REAL_KIND SELECTED_INT_KIND palauttamia arvoja seuraavaan tapaan: PROGRAM lajitesti IMPLICIT NONE INTEGER :: i, laji DO i = 1, 24 laji = SELECTED_INT_KIND(i) WRITE (*,*) i =,i, laji =,laji END DO END PROGRAM lajitesti Huomaa, että lajiparametrin arvot riippuvat käytetystä Fortran-kääntäjästä!
4. (5.6) Tulosta lausekkeen 1 3 ln(1 + 2) + 1 15 (2 + 2) 0.5214... arvo vähintään 6 desimaalin tarkkuudella. Käytä funktiokutsuja LOG ja SQRT. Tulosta lausekkeen arvo lisäksi suurimmalla kääntäjän tuntemalla reaalilukujen tarkkuudella.
Harjoitus 3: syöttö ja tulostus 1. (12.2) Seuraavassa esimerkissä on määritelty eri tavoilla Fortranin muotoilulausekkeita. Mitä ohjelma tekee? Mitkä määrittelyt ovat mielestäsi toimivimpia? PROGRAM muoto IMPLICIT NONE REAL :: x CHARACTER(LEN=11) :: form1 CHARACTER(LEN=*), PARAMETER :: & form2 = (F12.3,A) x = 12.0 form1 = (F12.3,A) WRITE (*, form1) x, hello WRITE (*, form2) 2*x, hi WRITE (*, (F12.3,A) ) 3*x, hi hi END PROGRAM muoto 2. (12.3) Kirjoita tulostuslauseet seuraaville taulukoille: REAL, DIMENSION(6,10) :: a INTEGER, DIMENSION(24) :: h CHARACTER(LEN=10), DIMENSION(6) :: mtaulu LOGICAL, DIMENSION(2) :: ehto COMPLEX, DIMENSION(20) :: z 3. (12.4) Kirjoita listan ohjaamat syöttörutiinit, jotka lukevat arvot edellisen tehtävän taulukoille. Anna esimerkki ohjelmasi syöttötiedostosta.
Harjoitus 4: ohjausrakenteet 1. (7.3) Ilmoita seuraavien DO-silmukkojen toistokertojen lukumäärä, silmukkamuuttujan i saamat arvot silmukan sisällä sekä silmukkamuuttujan arvo DO-rakenteen jälkeen. DO i = 1, 5 DO i = 5, 0, -1 DO i = 10, 1, -2 DO i = 0, 30, 7 DO i = 3, 2, 1 2. (7.2) Kirjoita SELECT CASE -rakenne, joka tekee kolme eri tyyppistä toimintoa riippuen siitä, onko kokonaislukumuuttuja negatiivinen, nolla tai jokin alkuluvuista (3, 5, 7, 11, 13). Muussa tapauksessa ei tehdä mitään. 3. (7.4) Kirjoita DO-silmukka joka summaa 100 annetun luvun neliöjuuret poislukien negatiiviset luvut ja lopettaa summauksen mikäli summattava luku on nolla. Tee kaksi versiota, joista toinen käyttää CYCLE-lausetta ja toinen ei. 4. (7.6) Tulosta muunnostaulu tuumista millimetreihin (1" = 25.4 mm). Aloita arvosta 0.0 ja tulosta arvoja puolen tuuman välein 12 tuumaan asti.
Harjoitus 5: proseduurien määrittely 1. (13.3) Kirjoita aliohjelma, joka muuntaa napakoordinaattiesityksen (r, φ) karteesisiksi koordinaateiksi (x, y). Kirjoita myös päinvastaisen muunnoksen tekevä aliohjelma. Käytä hyväksi määritelmiä sin φ = y/r, cos φ = x/r, r = x 2 + y 2. 2. (13.7) Kirjoita looginen funktio numerot, joka ilmoittaa, koostuuko argumenttina annettu merkkijono pelkästään numeromerkeistä. Käytä apuna standardifunktiota VERIFY. 3. Kirjoita funktio, joka laskee b-kantaisen logaritmin luvusta x. Muistutus: log b x = log 10 x/ log 10 b = ln x/ ln b Mieti myös funktion argumenteille b ja x sallimasi tyypit ja määrittelyalueet! 4. (8.5) Kirjoita rekursiivinen funktio kahden kokonaisluvun suurimman yhteisen tekijän laskemiseksi. Käytä hyväksi identiteettejä { syt(m, n) = syt(n, MOD(m, n)), syt(m, 0) = m. 5. (8.7) Kirjoita rekursiivinen funktio joka laskee
Tribonaccin lukuja : { x1 = 1, x 2 = 1, x 3 = 1, x n = x n 1 + x n 2 + x n 3. Laske x 12. Tutki laskenta-ajan käyttäytymistä n:n funktiona käyttämällä vaikkapa standardialiohjelmaa SYSTEM_CLOCK tai ohjelman ulkoista ajanmittausta (time-komento). Onko rutiini tehokas? Toteuta laskeminen myös silmukkarakenteen avulla. 6. Kirjoita funktio asteina(f, x), joka palauttaa standardifunktioiden ASIN, ACOS ja ATAN arvon asteina eikä radiaaneina. Käytä kyseisiä standardifunktioita määrittelemäsi funktion argumenttina f.
Harjoitus 6: proseduurin argumentit 1. (8.9) Luennolla esiteltiin rutiini FUNCTION igr skalaarifunktion f (x) integroimiseksi Gaussin integroimiskaavalla I b a 2 [ f ( a + b 2 b a 2 3 ) + f (a + b 2 missä integroimisväli on [a, b]. Lisää ohjelmaan valinnaiset argumentit siten että + b a ] 2 3 ), jos vasempaa integroimisrajaa a ei ole annettu, asetetaan a = 0, jos oikeaa integroimisrajaa b ei ole annettu, asetetaan b = a + 1. Testaa ohjelma ensin käyttämällä integrandina jotain standardifunktiota. 2. Vaihda nyt integrandi itse määritellyksi (ulkoiseksi) funktioksi. Määrittele sen kutsumuoto INTERFACE-lohkolla. 3. Lisätehtävä. Laajenna edellä määriteltyä rutiinia että käyttäjä voi valinnaisella argumentilla määritellä halutun integroimispisteiden määrän.
Harjoitus 7: taulukot 1. (11.1) Onko seuraava määrittely oikein? REAL DIMENSION(1:3,2:3) :: aa 2. (11.2) Määrittele kokonaislukutaulukko taulu, jossa on 3 riviä ja 4 saraketta. Alusta taulukon ensimmäiselle riville järjestyksessä vasemmalta oikealle kokonaisluvut 1-4, toiselle riville luvut 5-8 ja alimmalle riville joka sarakkeeseen luku -2. Tulosta taulu riveittäin siis yhdelle tulostusriville yhden taulukon rivin alkiot. 3. Muodosta vielä 3 8-kokonaislukutaulukko isotaulu, jossa on ensimmäiset 4 saraketta ovat identtisiä edellisen tehtävän taulukon taulu kanssa, ja 4 jälkimmäistä on saatu taulukon taulu sarakkeista kertomalla ne luvulla 3 ja lisäämällä tuloon 5. Käytä taulukko-operaatioita. 4. Kirjoita aliohjelma, joka osaa tehdä automaattisesti edellisessä tehtävässä kuvatun operaation. Aliohjelmalle syötetään pienempi taulukko, ja sen pitää palauttaa kooltaan kaksinkertainen taulukko, jonka alkiot on muodostettu edellä kuvatulla tavalla. 5. (11.4) Määrittele kaksi osoitinmuuttujaa. Aseta toinen osoittamaan yksiulotteiseen reaalilukutaulukkoon REAL, DIMENSION(10) :: x ja toinen saman taulukon kuudenteen alkioon.
6. (11.5) Käytä osoitinmuuttujia sijoittamaan edellisen tehtävän taulukon x järjestysluvultaan parillisiin alkioihin arvo 1.0 ja parittomiin alkioihin arvo 1.0. Taulukon arvojen tulee siten olla seuraavan listan mukaisia: (1.0, 1.0, 1.0, 1.0,...). 7. (11.6) Tee aliohjelma, joka palauttaa osoitinmuuttujassa halutun kokoisen taulukon. Anna taulukon koko aliohjelman argumenttina. Aliohjelmaa voisi käyttää esimerkiksi seuraavasti: CALL varaa(p, n)
Harjoitus 8: taulukkosyntaksi Muunna ohjelma table1.f (hakemistossa $DOC/f90-kurssi/esim/) siten, että siinä käytetään taulukkosyntaksia. Ohjelmassa esiintyvä rand palauttaa satunnaisluvun. Fortran 90:n vastaava rutiini on aliohjelma RANDOM_NUMBER. Kutsu CALL RANDOM_NUMBER(x) täyttää taulukon x satunnaisluvuilla, jotka ovat jakautuneet tasaisesti välille [0, 1). program table1 parameter (n=10) real b(n), c(n,n), d(n,n) do 10 i = 1,n b(i) = rand() do 10 j = 1,n c(j,i) = rand() 10 continue write (*,*) b do 20 i = 1,n do 20 j = 1,n d(j,i) = 1.0-c(j,i) 20 continue do 100 j = 2, n a = b(j) - b(j-1) do 100 i = 2, n c(i,j) = a * c(i-1,j)+d(i,j) 100 continue print *, answer =,c(n,n) end
Harjoitus 9: standardifunktiot Muunna ohjelma table2.f siten, että siinä käytetään taulukkosyntaksia ja standardifunktioita. Käännä alkuperäinen ohjelma f77:lla ja uusi f90:llä ja varmistu, että ne toimivat samalla tavalla. program table2 implicit none real x(100,100), y(100), z(100), a, s integer i,j,k do 1 j=1,100 y(j)=0.1*j do 1 i=1,100 x(i,j)=1.0+y(j) 1 continue do 2 j=1,100 s=0.0 do 3 i=1,100 s=s+x(i,j) 3 continue z(j)=sqrt(y(j))/s 2 continue do 5 j=1,100 5 if (z(j).lt. 0.001) z(j)=0.0 a=z(1) k=1 do 6 i=2,100 if (z(i).gt.a) then k=i a=z(i) endif 6 continue write (*,*) a,k end
Harjoitus 10: standardiproseduurit 1. (13.1) Ratkaise yhtälön x n = 1 kaikki kompleksiset juuret (vihje: e 2πk i = 1, k = 0, 1,...). 2. (13.4) Laske viisialkioisten reaalilukutyypin vektoreiden sisätulo standardifunktiolla DOT_PRODUCT. 3. (13.5) Laske 5 4 ja 4 3 -kokoisten matriisien A ja B tulo C = AB standardifunktiolla MATMUL. Laske myös tulo C T = B T A T, missä B T on matriisin B transpoosi jne. 4. (13.6) Etsi käyttäjän syöttämästä korkeintaan 80 merkin pituisesta merkkijonosta ensimmäisen numeromerkin sijainti käyttäen SCAN-funktiota.
Harjoitus 11: rakenteiset tyypit 1. (10.7) Määrittele rakenteinen tyyppi, joka voi tallettaa syntymäajan seuraavassa muodossa: 21 01 1990 Siis rakenteinen tyyppi sisältää kolme kokonaislukua, joilla on eri KIND-arvot: SELECTED_INT_KIND(2) ja SELECTED_INT_KIND(4). 2. (10.8) Lisää edellisen tehtävän rakenteiseen tyyppiin kenttä nimeä varten. 3. (10.9) Kirjoita funktio, joka palauttaa merkkijonossa nimen ja päivämäärän seuraavassa muodossa: Jaska Jokunen (01.01.1999) Käytä edellisessä tehtävässä luotua rakenteista tyyppiä. 4. (10.14) Kirjan kappaleessa 3.12 (sivu 33) on esitelty oppilaiden nimien ja arvosanojen tallentamiseen sopiva rakenteinen tyyppi kurssilainen. Määrittele moduuli, jossa on tämän rakenteisen tyypin käsittelyyn tarvittavia operaatioita. Määrittele oppilastietojen lukuja tulostusoperaatiot sekä arvosanojen keskiarvon laskemiseen soveltuva proseduuri.
Harjoitus 12: Moduulit 1. Määrittele pääohjelmassa rakenteinen tyyppi ja tyypin mukainen muuttuja. Alusta muuttuja ja välitä se ulkoiseen aliohjelmaan, joka tulostaa muuttujan komponentit. Käännä ja aja ohjelma. Mitä tapahtuu? Sijoita seuraavaksi aliohjelma moduuliin ja käännä ohjelma. Mitä tapahtuu? Korjaa ongelma. 2. Tee ohjelma, joka laskee yhteen ulkoisessa aliohjelmassa (funktiossa) kaksi reaalilukua. Tee moduuli, jossa määritellään lajiparametri kaksinkertaisen tarkkuuden reaaliluvuille. Käytä moduulia pääohjelman ja summausfunktion tarkkuuden määrittelyssä. 3. Muuta summausfunktiossa toinen yhteenlaskettavista kokonaisluvuksi. Käännä ja aja ohjelma. Mitä tapahtuu? Sijoita seuraavaksi summausfunktio moduuliin ja ota se käyttöön pääohjelmassa. Käännä ohjelma. Mitä nyt tapahtuu? Ja sitten muuta... 4. Toteuta luennoilla esitelty globaali piste-tyyppi, jossa on komponentteina kaksi reaalilukua (x- ja y-koordinaatti). Tee funktio, joka laskee kaksi pistettä yhteen. Kokeile moduulin toimintaa yksinkertaisella pääohjelmalla. 5. Olio-ohjelmointia. Yksi olio-ohjelmoinnin ideoista on,
että tietorakenteita voi käsitellä vain tietyillä funktioilla (C++-terminologiassa jäsenfunktiot), ja tietoalkioihin ei suoraan pääse käsiksi. Tällaisessa ohjelmoinnissa tietorakenteen käyttöliittymä on tarkasti määritelty, mikä vähentää ohjelmointivirheitä ja toisaalta helpottaa niiden paikallistamista. Muuta edellisen tehtävä piste-tyyppiä siten, että alkioihin x ja y ei pääse käsiksi moduulin ulkopuolella. Kokeile piste-tyyppisen muuttujan komponenttien alustusta ja tulostusta yksinkertaisella pääohjelmalla. Mitä tapahtuu? Lisää moduuliin aliohjelmat piste-muuttujien alustamista ja tulostamista varten. Kokeile moduulin toimintaa yksinkertaisella pääohjelmalla.
Harjoitus 13: geneeriset proseduurit 1. Jatkoa edellisen harjoituksen olio-ohjelmointiin. Muuta piste-moduulia siten, että alustuksen voi suorittaa sekä kokonais- että reaaliluvuilla käyttäen samaa aliohjelmakutsua. 2. Hakemistosta $DOC/f90-kurssi/esim/ löytyvä moduuli swapmod.f90 määrittelee geneerisen aliohjelman vaihda, jonka avulla voi vaihtaa reaali- ja merkkijonoargumenttien arvot keskenään. Muuta moduulia siten, että se toimii myös kokonaislukuargumenteilla. Entä yksi- ja kaksiulotteiset taulukkoargumentit? Käytä testaamisessa apuna pääohjelmaa swap.f90.
Harjoitus 14: Operaattorit 1. Sarjaan tai rinnakkain kytkettyjen vastusten resistanssit R s ja R r lasketaan kaavoilla R s = R 1 + R 2, 1/R r = 1/R 1 + 1/R 2. Määrittele reaalilukumuuttujia käsittelevät operaattorit, joita voidaan käyttää seuraavasti: REAL:: r, r1, r2 r = r1.sarjassa. r2 r = r1.rinnakkain. r2 2. Lisää piste-moduuliin operaattori + pisteiden alkioittaista yhteenlaskua ja operaattori.etaisyys. origosta laskettavan etäisyyden määrittämistä varten.
Harjoitus 15: F77 F90 1. Käännä Lempon Fortran 90 -kääntäjällä f90 jokin oma FORTRAN 77 -ohjelmasi käyttäen sarakesidonnaista syöttömuotoa. Voit myös kääntää esimerkkiohjelman $DOC/f90-kurssi/esim/avgvar.f. 2. Yritä muuntaa edellinen ohjelma Fortran 90:n vapaaseen syöttömuotoon. Muunna kirjaimella C tai merkillä * alkavat kommentit huutomerkillä alkaviksi jne. 3. Mitä eroa on seuraavan ohjelman käyttäytymisessä käytettäessä vapaata tai sarakesidonnaista formaattia? LOGICAL L L =.FALSE. Z = 0.0 Y = 1.0 IF (L) THEN Z = 1.0 ELSE Y = Z ENDIF PRINT *, Y, Z END Korjaa ohjelmassa olevat virheet.
Harjoitus 16: vanhat Fortran-koodit Tiedosto $DOC/f90-kurssi/esim/fzero.f sisältää aliohjelman, joka on peräisin kirjasta Kahaner, Moler, Nash: Numerical Methods and Software. Koodi on vanhaa Fortran 66:tta. Siivoa sitä mahdollisuuksien mukaan käyttämällä hyväksi Fortran 90:n rakenteita. Kirjoita testausta varten pieni ohjelma, joka kutsuu aliohjelmaa.