Obsah 1 Úvod 5 2 Fortran ve Windows a v Linuxu 7 2.1 Windows Prostředí MSDS............................ 7 2.2 Linux Intel Fortran................................ 9 Úkoly........................................ 10 3 Základy syntaxe 11 Příklady....................................... 13 Úkoly........................................ 14 4 Typy proměnných 15 4.1 Typy INTEGER a REAL.............................. 15 4.2 Převody typů.................................... 15 4.3 Typ COMPLEX................................... 16 4.4 Typ LOGICAL................................... 16 4.5 Typ CHARACTER.................................. 17 Úkoly........................................ 18 5 Příkazy organizační 19 5.1 Podmíněný příkaz.................................. 19 Příklady....................................... 20 Úkoly........................................ 20 5.2 Příkazy cyklu.................................... 21 Příklady....................................... 22 Úkoly........................................ 24 5.3 Náhodná čísla.................................... 25 Příklady....................................... 25 Úkoly........................................ 25 5.4 Příkaz CASE.................................... 26 1
Obsah Příklady....................................... 27 Úkoly........................................ 27 6 Pole I 29 Úkoly........................................ 30 7 Vstup a výstup, formát 31 Úkoly........................................ 33 8 Řetězce 35 Úkoly........................................ 36 9 Podprogramy 37 9.1 Subroutiny...................................... 37 9.2 Funkce........................................ 38 9.3 Standardní funkce.................................. 39 9.4 Parametry...................................... 40 9.4.1 Pole..................................... 40 9.4.2 Řetězce................................... 41 9.4.3 Popis INTENT............................... 41 Úkoly........................................ 42 10 Práce se soubory 43 Úkoly........................................ 46 11 Pole II 47 11.1 Konstrukce polí................................... 47 11.2 Části pole...................................... 48 11.3 Standardní podprogramy pro pole......................... 48 11.4 Dynamické alokace................................. 50 Úkoly........................................ 50 2
Obsah 12 Grafika 53 Úkoly........................................ 56 13 Moduly 57 Úkoly........................................ 58 14 Parametry podprogramů 59 14.1 Podprogramy jako parametry............................ 59 14.2 Volitelné parametry................................. 59 Úkoly........................................ 60 15 Uživatelský typ 61 Úkoly........................................ 62 16 Fortran 77 63 Literatura 65 3
Obsah 4
1 Úvod V těchto skriptech se pokusíme vysvětlit základy jazyka Fortran 90. Předcházející verze tohoto jazyka Fortran 77 bude zmíněna jen okrajově. Většina toho, co bude uvedeno v těchto skriptech, platí pro libovolný kompilátor jazyka Fortran, ale při praktickém programování je třeba pracovat s konkrétním kompilátorem. V těchto skriptech budeme jako příklad používat kompilátor Compaq Visual Fortran 6.6 (nebo jeho velice podobné předchůdce Digital Visual Fortran 5.0 a Microsoft Fortran 4.0) a kompilátor Intel Fortran Compiler 7.0. Jazyk Fortran je jedním z nejstarších programovacích jazyků. Je to jazyk, který byl a je určen především pro vědecko-technické výpočty. V tomto jazyce bylo naprogramováno velké množství numerických knihoven, které byly dokonale odladěny a neobsahují tedy žádné chyby. Navíc je ovládnutí jazyka ve verzi Fortran 90 poměrně jednoduché. Tento text obsahuje pouze základní prvky jazyka Fortran 90. Předpokládáme, že pokud si student osvojí jazyk na úrovni těchto skript, bude schopen si již další podrobnosti týkající se přesné syntaxe, standardních funkcí nebo některých pokročilých rysů tohoto jazyka, nastudovat sám za pomoci nápovědy a dokumentace, která je vždy součástí daného kompilátoru. Dalším zdrojem informací je internet, především pak stránky vyučujícího (jejich adresa se může měnit, proto bude vždy uvedena na počátku semestru). Zde bude možné nalézt řadu odkazů na studijní materiály a vzorové programy (na některé z nich se odkazuje i přímo v tomto textu). Aby si čtenář látku osvojil, je ovšem zapotřebí samostatně naprogramovat co nejvíce příkladů a splnit všechny úkoly. Programování se nelze nenaučit jinak než programováním. Proto také doporučujeme studentům pracovat maximálně samostatně, nebot cílem není jen programy průběžně odevzdávat, ale programování ovládat jako jeden z nástrojů jejich budoucí práce. Přitom nezáleží tolik na tom, zda budete Fortran používat ve Vaší budoucí práci. Řadu vlastností jazyka Fortran 90 najdete i v jiných programovacích jazycích a naučíte-li se jeden programovací jazyk, ovládnutí dalšího Vás již nebude stát tolik úsilí. 5
1 Úvod 6
2 Fortran ve Windows a v Linuxu 2.1 Windows Prostředí MSDS Kompilátor Compaq Visual Fortran 6.6 využívá vývojové prostředí Microsoft Developer Studio. Toto prostředí umožňuje psát, spouštět a ladit programy, je využíváno nejen jazykem Fortran, ale i jazykem Microsoft Visual C++ a dalšími. Při práci v tomto prostředí je třeba nejprve vytvořit tzv. projekt. Projekt obsahuje informace o tom, ze kterých souborů se má vytvářet výsledný program a další nastavení. Projekt v MSDS vytvoříme výběrem menu File New Projects, poté je třeba vybrat typ projektu. Pokud nepotřebujeme pracovat s grafikou, vybereme projekt Fortran Console Application, až budeme později pracovat s grafikou, vybereme projekt Fortran Standard or QuickWin Application. V pravé části dialogového okna vyplníme do Project name název projektu (tj. i název výsledného programu) a do Location místo na disku, kam se má projekt uložit. Prostředí vytvoří pro každý projekt nový adresář, do kterého se budou umíst ovat zdrojové texty a také výsledný program. Obrázek 1: Vytvoření projektu Po vytvoření projektu je třeba vytvořit zdrojové texty. To lze nejjednodušeji provést ikonou New 7
2 Fortran ve Windows a v Linuxu Text File a uložením souboru pod libovolným názvem s koncovkou.f90. Souborů může být libovolné množství. Po vytvoření souborů je třeba určit, které soubory patří do daného projektu. V menu vybereme Project Add to Project Files a poté vybereme potřebné soubory. Soubory, které jsou skutečně obsaženy v projektu jsou vidět v levé části okna MSDS v Source Files. Obrázek 2: Hotový projekt s vloženým zdrojovým textem Program musíme přeložit ( zkompilovat ), což provádíme tak, že v menu vybereme Build Build {název programu}, nebo stiskneme klávesu F7. Program spustíme bud uvnitř vývojového prostředí pomocí Build Execute {název programu}, nebo samostatně na příkazové řádce systému Windows napíšeme jméno přeloženého programu (tedy souboru s koncovkou.exe) v adresáři, kam se tento spustitelný soubor uložil. Rovněž je možné spustit program z vývojového prostředí stiskem kombinace kláves Ctrl+F5. Shrnutí postupu: Vytvoření projektu Vytvoření zdrojových souborů Přiřazení souborů do projektu 8
2 Fortran ve Windows a v Linuxu 2.2 Linux Intel Fortran Narozdíl od MSDS je Intel Fortran řádkový překladač a práce s ním je méně komfortní. Na druhé straně není nutné zakládat žádný projekt jako v MSDS a zdrojový kód lze rovnou přeložit a program spustit. Obrázek 3: Zdrojový kód v programu KWrite Obrázek 4: Překlad a spuštění programu hello 9
2 Fortran ve Windows a v Linuxu Zdrojový kód můžeme vytvořit v libovolném textovém editoru (např. KWrite nebo Emacs). Před samotným překladem je třeba hotový kód uložit na pevný disk do souboru s obvyklou příponou.f90. Překlad spustíme příkazem ifc -o nazev programu zdrojovy kod.f90, od verze překladače Intel Fortran Compiler for Linux 8.0 pak příkazem ifort -o nazev programu zdrojovy kod.f90, který zadáváme v konzolovém okně. Samotný program pak spustíme zadáním názvu programu na příkazové řádce. Shrnutí postupu: Vytvoření zdrojového kódu obyčejný textový soubor příponou.f90 Přeložení kódu řádkovým překladačem Spuštění programu Úkoly 1. Vytvořte program pomocí jednoho z předchozích postupů. Do zdrojového kódu napište: PROGRAM HELLO IMPLICIT NONE WRITE(*,*) Hello END PROGRAM Program zkompilujte a spust te. 2. Seznamte se pomocí nápovědy (na disku počítače i na internetu) a dostupné dokumentace s kompilátorem a vývojovým prostředím, se kterým budete dále pracovat. 10
3 Základy syntaxe Základní poznámky: Nerozlišují se malá a velká písmena, tj. Můžeme napsat PROGRAM, program i Program. Totéž se týká i jmen proměnných pokud proměnnou definujeme jako N, můžeme na ni odkazovat i jako n (z důvodu přehlednosti programu je však vhodné používat proměnnou tak, jak byla definována). Komentář je cokoliv co následuje po znaku vykřičník! až do konce řádku. Neexistuje víceřádkový komentář. Na každém řádku je jen jeden příkaz, který se neukončuje středníkem. Pokud je na řádku více příkazů, píšeme středník mezi ně. Pokud se příkaz nevejde na řádek, lze ho rozdělit na více řádků. Na konci řádků, které nejsou poslední, je třeba uvádět znak &. Soubor má většinou koncovku.f90. Několik základních pravidel: Každý program začíná klíčovým slovem PROGRAM za kterým následuje jednoslovný název programu. Jako druhý řádek vždy uvádějte IMPLICIT NONE. Tento příkaz není sice nezbytně nutné uvádět, ale pokud jej uvedeme, máme zaručenu důslednou kontrolu typů proměnných všechny proměnné musí být deklarovány v úvodu programové jednotky, jinak dojde k chybové hlášce kompilátoru. Program končí slovem END nebo lépe END PROGRAM, za kterým volitelně může následovat i název programu. Místo END PROGRAM můžeme psát ENDPROGRAM. Podobně je to i s ostatními příkazy, které jsou složeny ze dvou slov, např. místo END IF můžeme psát i ENDIF apod. Přivolání podprogramu je třeba před jeho názvem uvést CALL. Nejjednodušší program vypadá tedy třeba takto (samozřejmě vůbec nic nedělá): PROGRAM PRVNI_PROGRAM IMPLICIT NONE END PROGRAM 11
3 Základy syntaxe Základním prvkem jazyka jsou proměnné. Identifikátor proměnné může obsahovat písmena, číslice a znak podtržítko, musí ale začínat písmenem. Nerozlišují se malá a velká písmena. Proměnné mohou mít některý ze šesti typů, např. celá čísla mají typ INTEGER, reálná mají typ REAL, znaky a řetězce mají typ CHARACTER. Při deklaraci proměnné se nejdříve uvede typ a za ním následují identifikátory oddělené čárkami. Například INTEGER I REAL A,B CHARACTER C Proměnné nemají definovanou žádnou počáteční hodnotu. Počáteční hodnotu však můžeme zadat pomocí =. Specifikaci typu a proměnné je však třeba oddělit dvojicí dvojteček. Například INTEGER :: I=0 REAL :: PI=3.1415926535, x=1.0 CHARACTER :: CH= x Dvojtečky můžeme psát volitelně i v případě, že se počáteční hodnoty nezadávají a v následujícím textu tak budeme zpravidla činit. Pokud proměnná nemá měnit svojí hodnotu (konstanta), uvede se za specifikaci typu další specifikace PARAMETER. Například REAL, PARAMETER :: PI=3.1415926535897932384626433832795 INTEGER, PARAMETER :: N = 100, M = 3 CHARACTER, PARAMETER :: plus = + Základním způsobem práce s proměnnými jsou přiřazovací příkazy. Znak pro přiřazení je =, základní aritmetické operátory jsou + - * /. Pomocí operátoru ** lze umocňovat. Lze používat řadu standardních funkcí například SIN, COS nebo SQRT pro odmocninu. Parametry funkcí se zadávají do kulatých závorek. Všechny deklarace proměnných musí předcházet všem příkazům. Všechny proměnné je třeba deklarovat. PROGRAM DRUHY_PROGRAM! Toto je první komentář! Toto je take komentář IMPLICIT NONE! nic není deklarováno dopředu INTEGER :: I! celočíselná proměnná I REAL :: x,y,z! reálné proměnné x,y,z REAL :: Z! chyba, Z již bylo definováno x = y + z z = 1234*i! v pořádku, i je totéž co I z = SIN(y) y = SQRT(X) x = i**3! totéž co x = i*i*i 12
3 Základy syntaxe INTEGER :: n! chyba, deklarace musí být před příkazy y = SIN(3.14/2) i = -j! chyba j nebylo deklarováno END PROGRAM Základní příkazem pro výpis proměnné nebo výrazu na obrazovku je příkaz WRITE(*,*). Za tímto příkazem následují vypisované výrazy, které oddělujeme čárkou. Například WRITE(*,*) I WRITE(*,*) x+y, z Každý příkaz WRITE vypíše jeden řádek. Příkaz bude podrobněji vysvětlen později, nyní jen stručně poznamenejme, že první hvězdička zařídí výstup na obrazovku, druhá specifikuje, že formátování výstupu je volné a zařídí jej sám jazyk Fortran. Podobně, ale pro čtení, funguje příkaz READ. Tento příkaz načte údaje zadané z klávesnice do proměnných. Například READ(*,*) I READ(*,*) x, y READ(*,*) x+y! chyba - přečíst lze jen proměnnou, nikoli výraz Dalším často používaným typem jsou řetězce. Pro začátek vystačíme s konstantním řetězcem, který se uvádí jako text uzavřený do uvozovek nebo do apostrofů, například abc nebo Uplne cokoliv.. Mezi uvozovkami a apostrofy není žádný rozdíl. Význam hvězdiček je podobný jako u příkazu WRITE. Příklady Příklad: klasický program pro vypsání řetězce na obrazovku. PROGRAM HALLO IMPLICIT NONE WRITE(*,*) Hallo World! READ(*,*) END PROGRAM Příklad: jednoduchý program na řešení kvadratické rovnice ax 2 + bx + c = 0. PROGRAM ROVNICE IMPLICIT NONE REAL :: a,b,c! koeficienty rovnice 13
3 Základy syntaxe REAL :: x1,x2 REAL :: d! výsledky! diskriminant WRITE(*,*) zadej koeficienty a,b,c kvadraticke rovnice READ(*,*) a,b,c! načtení koeficientů d = b**2-4*a*c! výpočet diskriminantu x1 = ( -b + SQRT(d) )/(2*a)! výpočet kořenů x2 = ( -b - SQRT(d) )/(2*a) WRITE(*,*) koreny jsou:,x1,x2! výpis kořenů READ(*,*)! čekání na stisk ENTER END PROGRAM Program po skončení práce většinou ihned skončí. Aby si uživatel stihl přečíst údaje vypsané na obrazovku je třeba na konec programu zařadit čekání na stisk nějaké klávesy. Toho lze dosáhnou příkazem READ(*,*) bez parametrů, který počká na stisk klávesy ENTER. Poznámka: místo WRITE(*,*) x můžeme psát PRINT *, x. Místo READ(*,*) x můžeme psát READ *, x. Úkoly 1. Prostudujte a vyzkoušejte program: PROGRAM VYPOCET IMPLICIT NONE INTEGER :: a, b, c! deklarace, INTEGER! čtení z obrazovky WRITE(*,*) Zadej a: READ *,a WRITE(*,*) Zadej b: READ *,b! výpočet c=a+b! tisk WRITE(*,*) a+b =,c END PROGRAM VYPOCET 2. Načtěte z klávesnice dvě reálná čísla a vypište jejich součet, rozdíl, součin a podíl. 3. Sestavte program pro výpočet povrchu a objemu pravidelného šestibokého hranolu. 4. Napište program pro záměnu hodnot v proměnných p a q. (Kolik dalších proměnných budete na tuto výměnu nutně potřebovat?) 14
4 Typy proměnných Fortran zná šest typů proměnných: INTEGER, REAL, COMPLEX, CHARACTER, LOGICAL a uživatelsky definovaný typ. 4.1 Typy INTEGER a REAL Celá čísla mají typ INTEGER, reálná čísla typ REAL. Příkladem celých čísel jsou -1100, 0, 1 nebo 33. Reálná čísla musí mít desetinnou tečku nebo exponent. Příklady reálných čísel jsou.0, -1.10, +1E+30, 2E4, 1.12E-01 nebo 0.091. Znaménko plus je volitelné a nemusí se uvádět. Za typem lze v kulatých závorkách uvést přesnost proměnné. Větší hodnota odpovídá přesnější proměnné. Možné hodnoty závisí na konkrétním překladači. U Visual Fortranu jsou možné hodnoty: INTEGER(1), INTEGER(2), INTEGER(4), INTEGER(8), REAL(4), REAL(8). U Intel Fortranu navíc i REAL(16). Číslo v závorce v tomto případě znamená počet bytů, které zabere proměnná v paměti počítače. REAL(4) představuje jednoduchou přesnost reálných čísel, REAL(8) přesnost dvojitou, pro kterou se používá též označení DOUBLE PRECISION. Při práci s reálnými a celočíselnými proměnnými je třeba dát pozor na několik věcí: Výsledkem výrazu s celými čísly je opět celé číslo. Z toho plynou velice časté chyby, například 1/2 je nula nikoliv polovina. Jednu polovinu musíme zapsat jako 1.0/2 nebo 1/2.0 popř. 1.0/2.0. Pozor: pokud budeme pracovat ve dvojité přesnosti, je třeba sdělit tuto skutečnost i komplilátoru např. tím, že místo symbolu pro exponent E použijeme písmeno D. Výše uvedené příklady reálných čísel zapíšeme ve dvojité přesnosti například takto: 0.0D0, -1.10D0, 1D+30, 2D4, 1.12D-01 nebo 9.1D-02. Jednu polovinu tedy napíšeme správně například jako 1.D0/2.D0. Některé funkce předpokládají, že jejich parametr je reálné a ne celé číslo. Například SQRT(2) je chyba, SQRT(2.0) je správně. Existují i podobné standardní funkce pracující přímo ve dvojité přesnosti. Tyto funkce mají zpravidla jako první písmeno D, např. DSQRT, DSIN, DCOS, apod. a jako parametr je vhodné samozřejmě používat reálné konstanty a reálné proměnné ve dvojité přesnosti. 4.2 Převody typů Celé číslo na reálné číslo většinou není potřeba převádět, protože převod se uskuteční automaticky. Např. REAL :: r r = 1 15
4 Typy proměnných Někdy je však třeba převod explicitně uvést. To lze udělat pomocí převodní funkce REAL pro jednoduchou přesnost popř. DBLE pro přesnost dvojitou nebo vytvořením jednoduchého reálného výrazu. INTEGER :: i WRITE(*,*) SQRT(REAL(i)) WRITE(*,*) SQRT(1.0*i) WRITE(*,*) DSQRT(DBLE(i)) WRITE(*,*) DSQRT(1D0*i) Při převodu reálného čísla na celé je třeba určit jakým způsobem zaohrouhlujeme. Máme k dispozici několik podobných funkcí: INT odříznutí desetinné části, NINT nejbližší celé číslo, CEILING zaokrouhlení nahoru, FLOOR zaokrouhlení dolů. 4.3 Typ COMPLEX Typ COMPLEX reprezentuje komplexní proměnné. Podobně jako u reálných proměnných lze do kulatých závorek uvést přesnost proměnné. Komplexní konstanty se definují tak, že do kulatých závorek uvedeme reálnou a imaginární část oddělené čárkami, například komplexní jednotka je x=(0,1). Takto je třeba zapisovat komplexní číslo i v případě, že jej načítáme z klávesnice a takto je i vypsáno příkazem WRITE. Z reálných proměnných lze vytvořit komplexní pomocí funkce CMPLX. c = CMPLX(a,b) REAL a,b reálná a imaginární část COMPLEX c výsledné komplexní číslo Obráceně funkce REAL vrátí reálnou část komplexního čísla a funkce AIMAG imaginární část. Funkce CONJG vrátí číslo komplexně sdružené a funkce ABS vrátí absolutní hodnotu. Komplexní čísla můžeme sčítat, odčítat, násobit i dělit pouze za pomoci operátorů +, -, * a /, takže manipulace s komplexními čísly v jazyku Fortran je velmi jednoduchá. Rovněž funkce jako např. SIN nebo LOG umožňují jako parametr používat komplexní čísla. 4.4 Typ LOGICAL Proměnná typu LOGICAL reprezentuje logickou hodnotu. Může nabývat pouze dvou hodnot a to pravda.true. a nepravda.false.. Logické výrazy lze vytvářet pomocí srovnávacích (relačních) operátorů ==, >, <, >=, <=, /=. Pozor především na operátor je různé od /=, který se liší od jiných programovacích jazyků, např. od jazyka C. Ve výrazech lze také používat řadu logických spojek. Všechny tyto operátory začínají a končí tečkou. 16
4 Typy proměnných Operátor.AND..OR..NOT..EQV..NEQV. Logický význam a zároveň nebo neplatí je ekvivalentní není ekvivalentní Do proměnných typu LOGICAL lze například přiřadit konstantu (k=.true.), porovnání číselných proměnných (k= a<b) nebo výraz vytvořený pomocí logických operátorů (k= a<b.and. c==0.or. k). Proměnné tohoto typu se mohou vyskytovat v podmínce příkazu IF() (viz kapitola 5.1). Např. REAL :: a, b LOGICAL :: k k = a < b! následující je totéž IF (k)... IF (a < b)... Při tisku nebo načítání logické proměnné představuje hodnotu k=.true. písmeno T a hodnotu k=.false. písmeno F. 4.5 Typ CHARACTER Proměnné typu CHARACTER reprezentuje jeden znak. Pomocí tohoto typu se definují i řetězce, v tom případě za CHARACTER uvedeme do kulatých závorek délku řetězce, např. CHARACTER(15) :: s je řetězec, který má 15 znaků. Totéž můžeme zapsat i takto CHARACTER(LEN=15) :: s Řetězce i znaky se uvádějí do závorek nebo do apostrofů. Mezi závorkami a apostrofy není rozdíl. Řetězec, který začíná uvozovkami musí i uvozovkami končit, stejně je to s apostrofy. Toho lze využít pro řetězec obsahující třeba apostrof. Např. CHARACTER :: c CHARACTER(5) :: s c = a c = a s = ahoj! s = Can t Podrobněji bude o řetězcích pojednáno v kapitole 8. 17
4 Typy proměnných Úkoly 1. Prostudujte, zda program PROGRAM Vypocet IMPLICIT NONE INTEGER :: ai=5, bi=3, ci REAL :: ar=5.0, br=3.0, cr! Prvni vypocet a tisk ci=ai/bi cr=ar/br PRINT *, ci=ai/bi=, ci PRINT *, cr=ar/br=, cr! Druhy vypocet a tisk ci=ar/br cr=ai/bi PRINT *, ci=ar/br=, ci PRINT *, cr=ai/bi=, cr END PROGRAM Vypocet dává výsledky, jaké byste očekávali. Pokud ne, uvědomte si rozdíl mezi typy INTEGER a REAL a mezi celočíselným a reálným dělením. 2. Vyzkoušejte funkce INT, NINT, CEILING a FLOOR na reálných číslech 2.5 a 2.5. 3. Načtěte z klávesnice proměnnou a typu REAL a proměnné b a c typu COMPLEX. Proved te operace a b c, b + a c, b c a b/c. Výsledky vypište na obrazovku. 4. Načtěte z klávesnice hodnoty dvou logických proměnných L1 a L2, proved te s nimi postupně logické operace.and.,.or.,.not.,.eqv. a.neqv. a vytiskněte přehledně výsledky těchto operací, např. takto L1= T L2= F L1.AND. L2 = F L1.OR. L2 = T.NOT. L1 = F.NOT. L2 = T L1.EQV. L2 = F L1.NEQV. L2 = T 5. Napište program, který se postupně zeptá na Vaše jméno, datum, místo narození a Vaše záliby. Po obdržení jednotlivých informaci program pro kontrolu tyto informace vypíše na obrazovku. 18
5 Příkazy organizační 5.1 Podmíněný příkaz První organizační příkaz je podmíněný příkaz. Je ho základní forma je IF (logický_výraz) THEN příkazy ELSE příkazy END IF Místo END IF můžeme psát také ENDIF, mezera zde nemusí být. Část ELSE lze vypustit, zbyde pouze: IF (logický_výraz) THEN příkazy END IF Pokud chceme vykonat pouze jediný příkaz, lze THEN a ENDIF vynechat: IF (logický_výraz) příkaz Příkazy IF lze řetězit ve tvaru: IF (logický_výraz1) THEN... ELSE IF (logický_výraz2) THEN... ELSE... END IF Podmínky mohou být i vícenásobně vnořené, např. IF (logický_výraz1) THEN... IF (logický_výraz2) THEN... ELSE... END IF 19
5 Příkazy organizační ELSE... END IF O jednoznačné přiřazení příkazu ELSE k příslušnému IF se vždy postará umístění příkazu END IF, které uzavírá blok s podmínkou. Je vždy velice vhodné vnořené podmínky odsadit, aby i vizuálně při čtení programu bylo hned patrné, která část programu je vnořena a tvoří vnitřní část jiné podmínky. Nejčastější chybou je zapomenutí závorek, slova THEN nebo použití END místo ENDIF. Mezery mezi klíčovými slovy nejsou důležité a lze používat ENDIF i END IF, ELSEIF i ELSE IF. Příklady Příklad: zjištění znaménka zadaného čísla INTEGER :: a READ(*,*) a IF (a>0) THEN WRITE(*,*) cislo je kladne ELSE IF (a<0) THEN WRITE(*,*) cislo je zaporne ELSE WRITE(*,*) cislo je nula END IF Příklad: zjištění, zda obě čísla jsou kladná a zda jsou obě nenulová. Podmíněné příkazy mohou být i vnořené. INTEGER a,b READ(*,*) a,b IF (a>0.and. b>0) WRITE(*,*) obe cisla jsou kladna IF (b/=0) THEN IF (a/=0) THEN WRITE(*,*) obe cisla jsou nenulova END IF END IF Úkoly 1. Přepište následující vnořenou podmínku tak, abyste použili pouze jeden příkaz IF a správnost řešení ověřte v programu. 20
5 Příkazy organizační IF (b/=0) THEN IF (a/=0) THEN WRITE(*,*) obe cisla jsou nenulova END IF END IF 2. Přečtěte tři čísla a vypište největší z nich. 3. Přečtěte tři čísla a vypište ty dvě, které mají největší součet. 4. Změňte program pro hledání koeficientů kvadratické rovnice z kapitoly 3 tak, aby byly ošetřeny všechny možné případy (tj. řešením je jen jeden kořen, řešením jsou komplexní kořeny, je zadáno a = 0 nebo je zadáno a = b = 0). 5.2 Příkazy cyklu Dalším organizačním příkazem je příkaz cyklu. Nejjednodušší je nekonečný cyklus, který neustále opakuje příkazy v něm obsažené. Má tvar: DO příkazy END DO Kamkoliv do tohoto cyklu lze zařadit příkaz EXIT, který cyklus okamžitě ukončí, a program pokračuje za END DO. Dále lze použít příkaz CYCLE, který ukončí aktuální průchod cyklem a program pokračuje dalším průchodem za DO. Mezery mezi klíčovými slovy opět nejsou důležité a lze používat ENDDO i END DO. Další formou cyklu je tzv. cyklus úplný. Má tvar DO prom = start,konec,krok příkazy END DO Pokud krok není uveden, předpokládá se krok rovný jedné. I v tomto typu cyklu se mohou vyskytovat příkazy EXIT a CYCLE. Cyklus probíhá tak, že se nejprve nastaví hodnota proměnné prom na hodnotu start a provedou se postupně příkazy mezi DO a END DO. Potom se k proměnné prom přičte krok a opět se provádí všechny příkazy uvnitř cyklu. Toto se opakuje do té doby, než proměnná prom bude mít na začátku cyklu hodnotu větší než konec, nebo dokud není cyklus předčasně ukončen například pomocí EXIT. Poslední možností je cyklus podmíněný, který má tvar 21
5 Příkazy organizační DO WHILE (logický výraz) příkazy END DO Před provedením příkazů uvnitř podmíněného cyklu se vždy vyhodnocuje logický výraz (uzavřený v kulatých závorkách) na jeho počátku. Pokud je výsledkem logického výrazu.true., pak se příkazy uvnitř cyklu provedou a poté se logický výraz opět vyhodnotí, atd. V případě, že výsledkem logického výrazu je.false., příkazy uvnitř cyklu se neprovádějí a běh programu se přesouvá na první příkaz za cyklem. Cyklus tedy probíhá dokud je splněna podmínka v jeho úvodu. Předpokládá se samozřejmě, že uvnitř cyklu se nějakým způsobem mění proměnná, která je součástí logického výrazu. V opačném případě by byl totiž cyklus nekonečný (a běh programu bychom museli ukončit násilně). Rovněž cykly mohou být vnořené, příkazy EXIT a CYCLE se pak vztahují vždy k nejvnitřnějšímu cyklu DO. Opět je vhodné příkazy vnitřního cyklu odsadit, aby byla při čtení programu dobře patrná jeho struktura. Příklady Příklad 1: výpis čísel od jedné do desíti. DO i=1,10 WRITE(*,*) i END DO Příklad 2: výpis sudých čísel od dvou do dvaceti. DO i=2,20,2 WRITE(*,*) i END DO Totéž zapsáno pomocí cyklu DO WHILE vypadá následovně: DO i=1,10 WRITE(*,*) 2*i END DO Příklad 3: výpis čísel od desíti do jedné. DO i=10,1,-1 WRITE(*,*) i END DO 22
5 Příkazy organizační Totéž zapsáno naopak : DO i=1,10 WRITE(*,*) 11-i END DO Totéž zapsáno pomocí cyklu DO WHILE: i=10 DO WHILE (i>0) WRITE(*,*) i i=i-1 END DO Příklad 4: načítání čísel z klávesnice, konec při zadání nuly. DO READ(*,*) a WRITE(*,*) a IF (a == 0) EXIT END DO Totéž zapsáno pomocí cyklu DO WHILE: a=1! abychom měli jistotu, že je a nenulové DO WHILE (a/=0) READ(*,*) a WRITE(*,*) a END DO Příklad 5: načítání čísel z klávesnice, vypisování jen kladných, deset pokusů. DO i=1,10 READ(*,*) a IF (a < 0) CYCLE WRITE(*,*) i,a END DO Příklad 6: načítání čísel z klávesnice, vypisování jen kladných, konec po deseti vypsaných kladných číslech. 23
5 Příkazy organizační i = 0; DO READ(*,*) a IF (a < 0) CYCLE i = i + 1 WRITE(*,*) i,a IF (i == 10) EXIT END DO Opět zapíšeme cyklus i pomocí podmíněného cyklu: i = 0; DO WHILE (i<10) READ(*,*) a IF (a < 0) CYCLE i = i + 1 WRITE(*,*) i,a END DO Úkoly 1. Prostudujte pečlivě rozdíl mezi příklady 5 a 6. 2. Čtěte čísla z klávesnice, konec při zadání nuly. Nakonec vypište největší z nich, jejich počet a aritmetický průmět. 3. Čtěte čísla z klávesnice, konec při zadání nuly. Průběžně vypisujte dosud největší a nejmenší číslo. 4. Vypište všechny dvojice čísel menších než 5, každá dvojice se vypíše jen jednou ([3,4] je totéž co [4,3]). 5. Vypište všechny dvojice čísel menších než deset, jejichž součin je menší než 40. Nakonec vypište jejich počet. 6. Čtěte znaky z klávesnice dokud nestisknete klávesu x nebo X, potom program ukončete. 7. Napište program pro výpočet faktoriálu zadaného čísla n. 8. Napište program, který přečte na vstupu číslo X a najde číslo n, jehož faktoriál je nejbližší nižší číslo k X. (Návod na řešení: hledáme číslo n, pro které platí n X.) 24
5 Příkazy organizační 5.3 Náhodná čísla V řadě příkladů a úkolů, které budou dále uváděny, je třeba načítat čísla z klávesnice. Opakované zadávání řady čísel ale při ladění programu často zdržuje. Jako alternativu můžete používat vygenerování náhodných (přesněji pseudonáhodných) čísel. Reálné číslo z intervalu 0, 1) lze vygenerovat příkazem RANDOM NUMBER. REAL :: a CALL RANDOM_NUMBER(a) Takto vygenerovaná čísla budou při každém spuštění programu stejná. Pokud chceme, aby čísla byla různá, použijeme na začátku programu příkaz: CALL RANDOM_SEED() Pokud chceme generovat celá čísla v nějakém intervalu c, d, použijeme pro převod čísla a získaného výše pomocí RANDOM NUMBER vztah INT(a*(d-c+1)+c) tedy číslo z rozsahu 0, 1) vynásobíme rozsahem hodnot celých čísel a přičteme hodnotu prvního čísla v intervalu. Výslednému číslu odřízneme desetinnou část. Příklady Příklad: vygenerování 10 náhodných čísel, nalezení největšího z nich. INTEGER :: i REAL :: a, max_a CALL RANDOM_SEED() max_a = 0 DO i=1,10 CALL RANDOM_NUMBER(a) WRITE(*,*) a IF (a > max_a) max_a = a END DO WRITE (*,*) nejvetsi je, max_a Úkoly 1. Vygenerujte deset náhodných čísel a vypište ta z nich, která jsou v intervalu 0.6, 0.8. Napište na závěr, kolik vygenerovaných čísel bylo z tohoto intervalu. 25
5 Příkazy organizační 2. Generujte náhodná čísla tak dlouho, dokud vygenerované číslo nebude z intervalu 0.0, 0.1. Vypište toto číslo na obrazovku a vypište i počet pokusů, které vedly k vygenerování tohoto čísla. Předchozí pokus opakujte pětkrát. 3. Naprogramujte 20 hodů kostkou, tj. náhodně generujte číslo od 1 do 6 a pokus 20 krát opakujte. 5.4 Příkaz CASE Tento příkaz podle hodnoty výrazu provede provede určité příkazy. Syntaxe je následující SELECT CASE (výraz) CASE (hodnota1) příkazy1 CASE (hodnota2) příkazy2... CASE DEFAULT příkazy END SELECT Výraz musí být typu INTEGER, CHARACTER nebo LOGICAL. Pokud výraz nabývá uvedené hodnoty vykonají se následující příkazy a příkaz CASE skončí. V případě, že výraz se nerovná žádné hodnotě provedou se příkazy za CASE DEFAULT, tato část může ale chybět. Na místě hodnoty lze uvádět jednotlivé konstanty oddělené čárkami. Místo jednoduchých konstant lze uvádět i rozsahy. U rozsahu se uvádí dolní a horní mez oddělené dvojtečkou. Některá z mezí může chybět, rozsah se potom bere od začátku nebo do konce. Například INTEGER :: i,a SELECT CASE (i) CASE(0) a = 1 CASE(1,4,20) a = 2 CASE(5:19,21) a = 3 CASE(22:) a = 4 CASE DEFAULT a = 5 END SELECT 26
5 Příkazy organizační Příkaz CASE lze vždy nahradit řadou příkazů IF, příkaz CASE je však přehlednější a při vhodném překladači by měl být i rychlejší. Příklady Jednoduché menu: INTEGER :: a, b, p DO WRITE(*,*) zadej dve cisla READ(*,*) a, b WRITE(*,*) zadej operaci WRITE(*,*) 1 - soucet WRITE(*,*) 2 - rozdil WRITE(*,*) 0 - konec READ(*,*) p SELECT CASE (p) CASE(0) EXIT CASE(1) WRITE(*,*) a + b CASE(2) WRITE(*,*) a - b CASE DEFAULT WRITE(*,*) neznama operace END SELECT END DO Úkoly 1. Napište program, který přečte dvě čísla z klávesnice a podle výběru umožní provést některou z následujících operací: 1) součet 2) rozdíl 3) součin 4) podíl těchto dvou čísel. Tento proces se opakuje, dokud není stisknuta 0. 2. Upravte předchozí program tak, že vybíráme z menu podle kláves s pro součet, r pro rozdíl, x pro součin, p pro podíl a k pro konec programu. 3. Prostudujte a vyzkoušejte následující program. PROGRAM Semafor IMPLICIT NONE CHARACTER(LEN=7) :: barva 27
5 Příkazy organizační PRINT *, Mozne barvy semaforu: cervena, zluta, zelena PRINT *, Zadejte barvu svetla na semaforu: READ(*,*) barva IF (barva== cervena ) THEN WRITE(*,*) Stop! ELSE IF (barva== zluta ) THEN WRITE(*,*) Priprav se! ELSE IF (barva== zelena ) THEN WRITE(*,*) Jed! ELSE WRITE(*,*) Neplatny udaj! END IF END PROGRAM Semafor Upravte tento program pomocí příkazu CASE. 28
6 Pole I Pole se ve Fortranu deklarují tím, že se při deklaraci za identifikátor proměnné uvede do kulatých závorek rozměr, případně více rozměrů. Např. INTEGER :: a(10), b(3) REAL :: x(5,5) REAL :: y(3,3,3)! jednorozměrná celočíselná pole! dvojrozměrné reálné pole! trojrozměrné pole Ve Fortranu začínají indexy jedničkou. Pole b z posledního příkladu má tedy prvky b(1), b(2) a b(3). Rozměry se uvádějí do kulatých závorek a pokud je indexů více, oddělují se čárkou. Pokud chceme, aby indexy začínaly od jiné hodnoty, můžeme uvést počáteční i koncový index oddělené dvojtečkou. Například při deklaraci INTEGER c(-1:1) máme k dispozici prvky c(-1), c(0) a 0(1). Hodnoty, které určují velikost pole, musejí být dopředu známy. Jsou to bud celá čísla jako v předchozím příkladě, nebo celočíselné konstanty. Například INTEGER, PARAMETER :: N=100 INTEGER, PARAMETER :: M=30 INTEGER :: a(m), b(2*n) REAL :: x(m,m), y(2,n) Rozměry se uvádějí do kulatých závorek stejně jako rozměry při deklaraci. Např. INTEGER,PARAMETER :: n=10 INTEGER :: a(n), b(n,n) INTEGER :: i, j DO i=1, n a(i)=i ENDDO DO i=1, n DO j=1, n b(i,j) = 3.0*a(i) + SIN(0.5*j) ENDDO ENDDO Práce s poli je asi největší výhoda jazyka Fortran, proto je třeba tomuto tématu věnovat zvýšenou pozornost. Pokud použijeme identifikátor pole ve výrazu bez uvedení indexů, pracujeme s celým polem. Nejčastější použití je při inicializacích, například 29
6 Pole I REAL :: a(10), b(3,3) a = 0.0! nastaví celé pole na nulu b = 1! nastaví celé pole na jedničku Celá pole se mohou vyskytovat i na pravé straně výrazů. Operace se potom provádějí pro všechny jednotlivé prvky. Takto lze nahradit cyklus jediným příkazem. Oproti zápisu s cyklem můžeme při vhodném překladači dosáhnout i zrychlení výpočtu. Všechny pole v příkazu musí mít stejný počet indexů i stejnou velikost. Např. REAL :: a(10), b(3,3), c(0:9) a = 3*c + 4*c**2 a = SIN(c) + a/2 a = SQRT(b)! chyba, rozměry nesouhlasí! předchozí příkazy lze zapsat i cyklem DO i=1,10 a(i) = 3*c(i) + 4*c(i)**2 a(i) = SIN(c(i)) + a(i)/2 ENDDO Celá pole lze jediným příkazem i vypisovat a načítat. K předchozímu příkladu můžeme tedy doplnit WRITE(*,*) a WRITE(*,*) b Jestliže takto vypíšeme dvojrozměrné pole, prvky budou vypsány postupně po sloupcích. To souvisí s tím, že ve Fortranu jsou prvky ukládány po sloupcích. Tato vlastnost se projeví jen zřídka, ale občas je třeba ji brát v úvahu. Úkoly 1. Naplňte pole o 10 prvcích náhodnými čísly z intervalu 0, 1), prvky pole pro kontrolu vypište. 2. Sestavte program pro výpočet skalárního a vektorového součinu trojrozměrných vektorů. 3. Napište program, který bude simulovat vrhy hrací kostkou. Výsledky jednotlivých pokusů zaznamenávejte ve formě četnosti do pole o 6 prvcích. Proved te 1000 pokusů a spočítejte relativní četnosti jednotlivých výsledků a průměrnou hodnotu výsledků. 4. Napište program pro řešení soustavy maximálně 6 lineárních rovnic o 6 neznámých Gaussovou eliminační metodou. 30
7 Vstup a výstup, formát Základní použití příkazů READ(*,*) a WRITE(*,*) jsme již viděli. Výstup, který těmito příkazy dostaneme, je formátovaný standardním způsobem. Pokud chceme specifikovat vlastní formát, lze uvést popis formátu místo druhé hvězdičky. Popis formátu je řetězec, který začíná a končí kulatými závorkami. Uvnitř závorek je samotná specifikace formátu. První písmeno v popisu formátu určuje vypisovaný typ, například písmeno I slouží k výpisu proměnných typu INTEGER. Číslo za tímto písmenem většinou označuje na kolik znaků se má proměnná vypisovat. Například příkaz WRITE(*, (I3) ) a vypíše proměnnou a pomocí tří znaků. Pokud vypisujeme jedním příkazem více proměnných, jednotlivé příkazy se oddělují čárkami. Například WRITE(*, (I3,I5) ) a, b Pokud za sebou následuje více stejných formátů, lze zápis zjednodušit, tím že před písmeno uvedeme číslo označující počet opakování daného formátu, například WRITE(*, (4I3) ) a, b, c, d Formátů existuje celá řada: Iw výpis čísla typu INTEGER w je celé číslo bez znaménka udávající počet znaků výpisu Tvar údaje I lze uvést i ve formě Iw.m, kde na vstupu se.m ignoruje a při výstupu udává minimální počet číslic. Má-li jich číslo méně, údaj se při tisku doplní zleva nulami. Musí platit 0 m w. Bw, Ow, Zw výpis čísla typu INTEGER Jedná se o modifikaci tvaru údaje I pro celá čísla ve dvojkové, osmičkové a šestnáctkové soustavě. Fw.d výpis čísla typu REAL s pevnou řádovou čárkou w je celé číslo bez znaménka udávající počet znaků výpisu d udává počet desetinných míst. Ew.d výpis čísla typu REAL s exponentem w je celé číslo bez znaménka udávající počet znaků výpisu d udává počet desetinných míst Tvar údaje E lze uvést i ve formě Ew.dEe. Na vstupu nemá část Ee význam. Při výstupu e určuje počet číslic exponentu. 31
7 Vstup a výstup, formát EN ( engineering ) Je ekvivalentní tvaru údaje E s jedinou výhradou, že exponent na výstupu bude vždy dělitelný třemi. ES ( scientific ) Je ekvivalentní tvaru údaje E s výhradou, že mantisa bude na výstupu vždy normalizována v rozmezí 1, 10). Lw výpis čísla typu LOGICAL w je celé číslo bez znaménka udávající počet znaků výpisu nx vynechání n znaků na vstupu, nebo výpis n mezer. Aw výpis proměnné typu CHARACETER w udává počet znaků, které se přečtou do řetězce nebo které se z řetězce vypíší. G, Gw, Gw.d, Gw.dEe vybere vhodnější z tvarů F, E, I, L, A Tc skok na c-tý znak v řádku TLc skok o c znaků doleva TRc skok o c znaků doprava (totéž co Xc) Místo specifikace formátu lze přímo uvést řetězec, který se má vypsat. WRITE(*, ( a =, I3) ) a WRITE(*, (a, I3) ) a =, a! totéž co předchozí příkaz Při výpisu kladných čísel se implicitně kladné znaménko nevypisuje. Pokud chceme, aby se plus vypisovalo, uvedeme specifikaci SP. Pokud chceme vypisování znaménka plus zrušit, uvedeme SS. Například WRITE(*, (SP, I4, SS, I4) ) a, a Počet specifikací ve formátu většinou odpovídá počtu vypisovaných proměnných. Pokud je proměnných více než je uvedeno ve formátu, formát se začne používat opět od začátku. Pokud je naopak proměnných méně, zbytek formátu se ignoruje. Vyzkoušejte WRITE(*, ( a =,I3, b =,I3, c =,I3) ) a, b, c WRITE(*, ( a =,I3, b =,I3, c =,I3) ) a, b, c, d, e WRITE(*, ( a =,I3, b =,I3, c =,I3) ) a, b Každý příkaz READ a WRITE vypisuje nebo čte jeden řádek. Jestliže však chceme na jeden řádek psát pomocí více příkazů je toto chování nežádoucí. Když chceme zabránit přechodu na nový řádek, uvedeme jako třetí parametr příkazu WRITE parametr ADVANCE= NO. Například 32
7 Vstup a výstup, formát WRITE(*, (2I3) ) a, b! lze napsat také pomocí dvou příkazů WRITE(*, (I3),advance= no ) a WRITE(*, (I3) ) b Pokud by se některý formát několikrát opakoval, můžeme využít příkazu FORMAT, a potom stačí volat návěští tohoto formátu, tedy pouze (v celém souboru jednoznačně dané) číslo, které je uvedeno před příkazem FORMAT. Vše lze pochopit z následující ukázky: INTEGER :: i1, j1, i2, j2 REAL :: x1, y1, x2, y2... WRITE(*, FMT=100) i1, i2, x1, y1 WRITE(*, FMT=100) j1, j2, x2, y2 100 FORMAT( od =,I3, do =,I3, x =,ES11.3, y =,ES11.3) Uvedený formát jsme v ukázce využili dvakrát a nemusíme tak např. opakovat delší výpis řetězců, kterými jednotlivé proměnné popisujeme. Další výhodou je, že v případě, když chceme změnit najednou několik výstupů, změníme vlastně jen jeden příkaz. Příkazy FORMAT je vhodné bud umístit v blízkosti výpisů, nebo je soustředit všechny hromadně do jednoho místa programu (s příslušným komentářem popisujícím účel jednotlivých příkazů). Úkoly 1. Zobrazte co nejpřehledněji tabulku s hodnotami sinu, kosinu, tangens a kotangens úhlů 0, π/6, π/4, π/3, 3π/2 a π/2. Hodnoty funkcí vypisujte bez exponentu s přesností na pět desetinných míst. 2. Naprogramujte svislý pád tělesa: zadejte z klávesnice výšku tělesa nad zemským povrchem (zabraňte přitom přechodu kurzoru na další řádek) a nechte těleso padat volným pádem. V časech t = 0, 1,... sekund vypisujte čas, hodnotu výšky nad povrchem a okamžitou rychlost tělesa. V okamžiku dopadu tělesa na zemský povrch výpis ukončete. Hodnoty výšky a rychlosti vypisujte s přesností na tři desetinná místa v zápisu s exponentem. 33
7 Vstup a výstup, formát 34
8 Řetězce Řetězce mají pevnou délku, s jakou byly nadeklarovány. To má výhodu v tom, že není třeba zabývat se složitým alokováním paměti při změně délky řetězce. Naopak nevýhoda spočívá v poněkud nezvyklé práci oproti jiným jazykům. Pokud přiřadíme do řetězce řetězec kratší délky, zbytek se doplní mezerami. Pokud přiřadíme řetězec delší konec se ignoruje. Toto chování je vidět na následujícím příkladu, před a za řetězcem vypisujeme dvojtečky, aby bylo vidět délku řetězce. CHARACTER(5) s s= abc WRITE(*,*) :, s, :! výsledek :abc : s= 1234567 WRITE(*,*) :, s, :! výsledek :12345: Pro řetězce existuje operátor //, který spojí dva řetězce do jednoho. Například WRITE(*,*) abc // 12. Často je třeba pracovat s částí řetězce. K tomu slouží výraz tvaru retezec(start:konec). Informaci o začátku resp. o konci můžeme vynechat, podřetězec se potom vezme od začátku rep. do konce původního řetězce. Například CHARACTER(6) s s= abcdef WRITE(*,*) s(2:4) WRITE(*,*) s(:4) WRITE(*,*) s(2:)! výsledek bcd! výsledek abcd! výsledek bcdef Ve Fortranu řetězec není pole znaků. Na jeden znak se nelze odkázat takto s(i), ale je třeba použít způsob s(i:i). Pro práci s řetězci existuje celá řada příkazů, k těm základním patří: l = LEN(s) Zjistí délku řetězce CHARACTER(*) s řetězec INTEGER l délka řetězce l = LEN TRIM(s) Zjistí délku řetězce bez koncových mezer CHARACTER(*) s řetězec INTEGER l délka řetězce bez koncových mezer ss = TRIM(s) odstranění mezer na konci textového řetězce CHARACTER(*) s řetězec CHARACTER(*) ss řetězec s bez koncových mezer, tj. kratší řetězec 35
8 Řetězce Pro jednoho převod znaku na číslo (pořadí v ASCII tabulce) a zpět slouží tyto funkce: c = CHAR(i) převede čísla na znak INTEGER i číslo znaku CHARACTER c znak na pozici i v tabulce znaků i = ICHAR(c) převod znaku na číslo (opak CHAR) CHARACTER c znak INTEGER i pořadí znaku c v tabulce znaků Jako příklad uved me kód, který do řádku vypíše abecedu: DO i=1, 26 WRITE(*, (a),advance= no ) char(i-1+ichar( A )) ENDDO WRITE(*,*) Další příklad převede malá písmena v řetězci na velká (využíváme příkazu rozděleného na dva řádky): DO i=1, len(s) IF (s(i:i)>= a.and.s(i:i)<= z ) s(i:i) = char(ichar(s(i:i))-ichar( a )+ichar( A )) ENDDO & Pro převod řetězce na číslo a obráceně slouží již známé příkazy READ a WRITE. Jako první parametr místo hvězdičky nebo čísla jednotky tentokrát uvedeme identifikátor řetězce čteme a zapisujeme do řetězce. Například CHARACTER(5) s REAL :: x=3.14159 WRITE(s, (f5.2) ) x! výsledek s = 3.14 s= 12.34 READ(s, (f5.2) ) x! výsledek x = 12.34 Úkoly 1. Vytvořte tabulku ASCII znaků od 32 do 127 (zobrazte vedle sebe vždy číslo a jemu odpovídající znak v ASCII tabulce). Tabulku vhodně naformátujte, aby se vešla na jednu obrazovku. Zjistěte dále, jaké znaky se skrývají pod čísly 128 až 255. 2. Přečtěte řetězec z klávesnice a změňte v něm všechna velká písmena na malá. 3. Přečtěte řetězec z klávesnice (může být i hodně dlouhý, např. 200 znaků) a nakopírujte tento řetězec do jiného, přičemž odstraňte z textu (tedy i uvnitř řetězce) všechny mezery. 36
9 Podprogramy Ve Fortranu se rozlišují dva typy podprogramů: FUNCTION podprogramy vracející hodnotu funkce SUBROUTINE podprogramy nevracející hodnotu subroutiny (nebo též vlastní procedury) 9.1 Subroutiny Syntaxe subroutin je následující SUBROUTINE nazev(parametry)... deklarace... prikazy... END SUBROUTINE V hlavičce funkce se uvádějí pouze identifikátory parametrů, jejich typy se specifikují uvnitř subroutiny jako u jiných proměnných. Uvnitř subroutiny se nejprve uvádějí deklarace lokálních proměnných a poté výkonné příkazy, podobně jako v hlavním programu. Při volání subroutiny je nutné před identifikátor subroutiny uvést příkaz CALL: CALL nazev(skutecne_parametry) Všechny parametry se předávají tzv. odkazem. Jakékoliv změny parametrů uvnitř subroutiny se projeví i ve skutečných parametrech. Subroutiny se umist ují na konec programu za výkonné příkazy. Od výkonných příkazů se oddělují příkazem CONTAINS. Příklad: PROGRAM pythagoras! ze zadaných délek odvěsen spočítá délku přepony IMPLICIT NONE REAL :: x,y,z! délky stran WRITE(*,*) zadej odvesny READ(*,*) x,y CALL pythag(x,y,z) WRITE(*,*) prepona je :, z READ(*,*)! načtení délek odvěsen! zavolání podprogramu! výpis přepony! čekání na Enter CONTAINS 37
9 Podprogramy!================================================= SUBROUTINE pythag(a,b,c) REAL :: a,b,c c=sqrt(a**2+b**2)! pythagorova veta END SUBROUTINE!================================================= END PROGRAM Vykonávání podprogramu lze kdykoliv ukončit příkazem RETURN. Proměnná deklarovaná uvnitř podprogramu je lokální v hlavním programu ani v ostatních podprogramech ji nelze použít. Naopak proměnné deklarované v hlavním programu jsou globální lze je použít i uvnitř jakéhokoliv podprogramu (pokud ovšem není deklarována proměnná stejného jména). 9.2 Funkce Funkce jsou velice podobné subroutinám, jejich syntaxe je následující FUNCTION nazev(parametry) deklarace příkazy END FUNCTION Ve Fortranu vystupuje identifikátor funkce jako proměnná a výsledek se do této proměnné přiřadí. Předchozí příklad zapsaný jako funkce FUNCTION fpythag(a,b) REAL :: fpythag REAL :: a, b fpythag = SQRT(a**2+b**2) END FUNCTION Funkce se volá uvedením identifikátoru se skutečnými parametry v aritmetickém příkazu. Například z=fpythag(x,y)! nebo WRITE(*,*) fpythag(x,y) Podprogramy nelze běžně volat rekurzivně. Pokud chceme použít rekurzivní volání je třeba před slovo FUNCTION nebo SUBROUTINE uvést RECURSIVE, např. 38
9 Podprogramy RECURSIVE FUNCTION Recfun(x) REAL :: Recfun, x... END FUNCTION Tuto funkci můžeme nyní volat i vícekrát najednou. Pokud nevolá funkce přímo sama sebe, ale prostřednictvím jiného podprogramu, stačí uvedená deklarace. Pro přímé rekursivní volání je třeba ještě oddělit název funkce potřebný pro další volání od výsledku výpočtu funkční hodnoty. K tomu slouží klíčové slovo RESULT udávající název proměnné, do níž bude výsledek uložen. Zřejmé to bude z příkladu na výpočet faktoriálu: RECURSIVE FUNCTION Factorial(n) RESULT(res) INTEGER :: res, n IF (n==1) THEN res = 1 ELSE res = n*factorial(n-1) END IF END FUNCTION 9.3 Standardní funkce Součástí jazyka Fortran 90 je velké množství standardních funkcí a subroutin, které jsou přímo implementovány do jazyka. Není tedy nutné při jejich používání spojovat programový kód s různými knihovnami nebo tyto knihovny volat z programu. Nejčastěji budeme používat matematické standardní funkce. Jejich význam je většinou patrný již z jejich názvu: ACOS(x), ASIN(x), ATAN(x), ATAN2(y,x), COS(x), COSH(x), EXP(x), LOG(x), LOG10(x), SIN(x), SINH(x), SQRT(x), TAN(x), TANH(x). Proměnná x je ve všech případech reálné (může však být i komplexní) číslo. Upozorněme na to, že funkce LOG(x) představuje logaritmus přirozený a funkce LOG10(x) logaritmus dekadický. Jedině funkce ATAN2(y,x) má dva parametry a vrací úhel, který svírá polopřímka procházející bodem (x, y) s osou x. Dalšími často používanými standardními funkcemi jsou funkce numerické. Z nich jmenujme tyto základní: ABS(x) AIMAG(z) AINT(x) ANINT(x) CEILING(x) CMPLX(x,y) absolutní hodnota imaginární část komplexního čísla reálné číslo oříznuté na celé reálné číslo zaokrouhlené na celé nejmenší číslo větší nebo rovno x vytvoří komplexní číslo x + iy 39
9 Podprogramy CONJG(z) komplexně sdružené komplexní číslo DBLE(x) převádí celé, reálné či komplexní číslo na dvojitou přesnost DIM(x,y) kladný rozdíl obou čísel FLOOR(x) největší číslo menší nebo rovno x INT(x) celé číslo vzniklé oříznutím desetinné části MAX(x1,x2,...) maximum z čísel x 1, x 2,... MIN(x1,x2,...) minimum z čísel x 1, x 2,... MOD(a,p) zbytek po dělení (má znaménko prvního čísla) MODULO(a,p) dělení modulo (má znaménko druhého čísla) NINT(x) nejbližší celé číslo REAL(x) převod celé, reálné či komplexní číslo na číslo REAL(4) SIGN(x,y) hodnota prvního čísla se znaménkem druhého Pro přesnější popis funkcí doporučujeme prostudovat dokumentaci a s funkcemi experimentovat. 9.4 Parametry Předávání skalárních proměnných jsme již viděli. Předávání polí jako parametrů je velice podobné, v hlavičce podprogramu i při jeho volání se uvede pouze identifikátor pole. Při deklaraci pole uvnitř podprogramu máme ale několik možností. 9.4.1 Pole První možností je úplná definice pole, stejná jako globálních polí. Například SUBROUTINE subr(a) INTEGER :: a(10) Tento způsob předávání pole není příliš vhodný, protože podprogram umožňuje pracovat pouze s polem jedné délky a není tudíž příliš univerzální. Druhou možností je předávat spolu s polem i jeho velikost, například SUBROUTINE subr(a,n) INTEGER :: n INTEGER :: a(n) Tento způsob se liší od deklarací, které jsme viděli doposud, protože proměnná n není konstantní, ale může se měnit. Toto lze použít nejen u formálních parametrů, ale i u lokálních proměnných. Pozor na to, aby deklarace meze předcházela deklaraci pole. Třetí možností je uvést místo mezí pole dvojtečky, například 40
9 Podprogramy SUBROUTINE subr(a) INTEGER :: a(:) Velikost takového pole bude stejná jako velikost pole, které předáme jako parametr při volání podprogramu. Tento způsob předávání pole by měl být využíván co nejvíce. 9.4.2 Řetězce Řetězce lze předávat podobně jako pole s konstantní délkou, například SUBROUTINE subr(s) CHARACTER s(5) Vhodnější ale je, uvést místo délky hvězdičku. Délka řetězce je potom stejná jako u skutečného parametru, například SUBROUTINE subr(s) CHARACTER s(*) 9.4.3 Popis INTENT Podprogramu můžeme říci, zda proměnná, která je parametrem, je na vstupu známa, či zda její hodnota je neznámá a bude teprve vypočítána uvnitř podprogramu, nebo zda se jedná o proměnnou, jejíž hodnotu známe a uvnitř podprogramu se tato hodnota změní. K tomu slouží popis INTENT, který má následující tři možné hodnoty: IN parametr je vstupní, jeho hodnota nesmí být uvnitř podprogramu změněna. OUT parametr je výstupní, jeho hodnota není na začátku známa, bude uvnitř podprogramu určena a využita v části programu, ze které podprogram voláme. Parametr je tedy určen pro přenos výsledku z podprogramu ven. INOUT parametr je vstupně-výstupní, jeho hodnota je definována při vstupu do podprogramu, může být uvnitř podprogramu změněna a předá se do místa volání podprogramu, kde může být dále využita. Atribut INOUT je nastaven jako implicitní a není jej třeba uvádět. Například ve výše uvedené subroutině na výpočet přepony pravoúhlého trojúhelníka bychom popis INTENT použili následovně: SUBROUTINE pythag(a,b,c) REAL, INTENT(IN) :: a,b! vstupní parametry REAL, INTENT(OUT) :: c! výstupní parametr = výsledek c=sqrt(a**2+b**2)! pythagorova veta END SUBROUTINE 41
9 Podprogramy V mnohých případech tímto způsobem se v programu vyvarujeme chyb a program bude navíc možné při překladu i lépe optimalizovat. Shrnutí zásad pro práci s podprogramy: Subroutiny se volají příkazem CALL Podprogramy nelze volat rekurzivně, pouze s RECURSIVE Podprogramy se umist ují za CONTAINS Pro parametry podprogramu uvádíme popis INTENT Subroutinu lze ukončit příkazem RETURN Úkoly 1. Vytvořte program, který spočítá průměr pouze lichých čísel z čísel, které zadává uživatel. Při zadání záporného čísla se program ukončí. Použijte funkci modulo(a,b) a příkazy CYCLE a EXIT. 2. Napište program, ve kterém použijete všechny standardní funkce uvedené v kapitole 9.3. 3. Napište program pro výpočet druhé mocniny pomocí vlastní funkce. Program pak rozšiřte pro výpočet n-té mocniny (n přirozené). 4. Přepište program pro výpočet skalárního a vektorového součinu z kapitoly 6 pomocí podprogramů. Jako parametr předávejte celé pole. 5. Napište rekurzivní podprogram na výpočet rekurentně zadané posloupnosti, jejíž první člen je roven x 1 = 0.5 a n-tý člen této posloupnosti je dán jako násobek předchozího členu vztahem x n = x n 1 1.5. 6. Přepište úlohy 2 resp. 3 z kapitoly 8 tak, že změnu velkých písmen na malá resp. vymazání všech mezer z řetězce zabezpečí podprogramy. 42
10 Práce se soubory V této kapitole probereme práci s nejjednodušším typem souborů s textovými soubory. Každému soubor, se kterým v jazyce Fortran pracujeme, přiřadíme tzv. číslo jednotky. Práci se souborem začínáme příkazem OPEN. První parametr tohoto příkazu je číslo jednotky a druhý parametr je řetězec obsahující název souboru přiřazený do parametru FILE=. Například OPEN(5, FILE= data.txt ) Před prvním parametrem může volitelně být UNIT=, tj. výše uvedený příkaz je tudíž totožný s příkazem OPEN(UNIT=5, FILE= data.txt ) Při otevření souboru se nerozlišuje, zda ze souboru budeme číst nebo do něj zapisovat. Pokud soubor existuje, příkaz OPEN ho otevře a nastaví aktuální pozici na jeho začátek. Pokud soubor neexistuje, příkaz OPEN vytvoří soubor nulové délky a nastaví aktuální pozici na jeho začátek. Práce se souborem se ukončí příkazem CLOSE, který má jediný povinný parametr, a tím je číslo jednotky. Například CLOSE(5) Podobně jako u příkazu OPEN, můžeme i zde volitelně napsat před číslo jednotky specifikátor UNIT=. Pokud je soubor otevřen, můžeme z něj načítat příkazem READ nebo do něj zapisovat příkazem WRITE, stejně jako čteme z klávesnice nebo vypisujeme na obrazovku. Jako první parametr těchto příkazů uvádíme místo hvězdičky číslo jednotky. Například WRITE(5,*) i WRITE(5, (I4,F5.2) ) j, x READ(6,*) i, j, k Zda se nacházíme na konci souboru, lze zjistit funkcí EOF end of file. Tato funkce má jako parametr číslo jednotky a vrací.true., pokud je aktuální pozice na konci souboru a.false. jinak. Například následující kód načte všechna čísla ze souboru a vypíše jejich počet. OPEN(4, FILE= data.txt ) n=0 DO 43
10 Práce se soubory IF (EOF(4)) EXIT READ(4,*) x n=n+1 END DO CLOSE(4) WRITE(*,*) n S výše uvedenými několika příkazy můžeme vystačit ve většině případů jednoduché práce se soubory. Uvedeme ještě několik parametrů, kterými můžeme ovlivnit vstup a výstup do souboru. Při otevírání souboru můžeme pomocí řetězce přiřazeného do parametru STATUS= sdělit programu stav souboru v okamžiku jeho otevírání. Pokud zadáme hodnotu NEW, jedná se o soubor, který bude programem nově vytvořen (tento soubor nesmí existovat). Pokud zadáme hodnotu OLD, jedná se o soubor, který již existuje (tento soubor musí existovat). Hodnota REPLACE znamená nahrazení souboru a zajistí vytvoření nového souboru v případě, že neexistuje, nebo pokud soubor existuje smazání tohoto souboru a vytvoření nového souboru se stejným jménem. Volbu SCRATCH můžeme použít pro nepojmenovaný nový dočasný soubor, který se po zavření souboru nebo po ukončení programu automaticky smaže. Pokud neuvedeme parametr STATUS=, je mu přiřazena hodnota UNKNOWN. Ta znamená, že soubor může, ale nemusí, existovat. Pokud soubor neexistuje, vytvoří se nový soubor. Jako příklad otevřeme soubor vystup.txt. Pokud soubor neexistuje, bude vytvořen, pokud existuje, bude nahrazen: OPEN(7, FILE= vystup.txt, STATUS= REPLACE ) Po otevření se mění stav souboru na OLD. Pokud bychom chtěli otevřít existující soubor a navázat na něj, tj. další hodnoty přidávat na konec souboru, můžeme využít parametrposition=, kterému přiřadíme hodnotu APPEND. Tedy mějme například již vytvořen soubor data.txt. Chceme-li nyní připsat další výstup za konec tohoto souboru, otevřeme jej následovně: OPEN(8, FILE= data.txt, STATUS= OLD, POSITION= APPEND ) Pokud bychom v předchozím příkazu zvolili STATUS= UNKNOWN, docílili bychom stejného efektu a navíc by soubor byl v případě, že neexistuje, vytvořen (a do souboru by se tedy zapisovalo od jeho začátku, který je při prvním otevření souboru totožný s jeho koncem). Parametr POSITION= může ještě nabývat hodnotu REWIND pro pozici přesunutou na začátek souboru a ASIS, která ponechává pozici bez změny, pokud byl soubor již připojen. Abychom např. zabránili neúmyslnému zápisu do souboru, máme možnost pomocí parametru ACTION= určit, zda soubor použijeme jen pro čtení (ACTION= READ ), jen pro zápis (ACTION= WRITE ) nebo bez omezení pro čtení i pro zápis (ACTION= READWRITE ). Poslední hodnota je zpravidla (ne však nutně) nastavena jako implicitní. V následujícím příkladu otevřeme již existující soubor katalog.txt jen pro čtení: 44
10 Práce se soubory OPEN(9, FILE= katalog.txt, STATUS= OLD, ACTION= READ ) Rovněž i u příkaz CLOSE můžeme použít parametr STATUS=, který určuje co se stane, když soubor zavřeme. Hodnota KEEP ponechá soubor na disku, zatímco zvolíme-li DELETE, soubor bude po svém zavření vymazán. Implicitně je nastavena hodnota KEEP, to se ovšem nevztahuje na dočasné soubory, které byly otevřeny příkazem OPEN jako STATUS= SCRATCH. Pokud potřebujeme zjistit stav souboru, at již otevřeného nebo jen souboru někde na disku, můžeme použít příkaz INQUIRY. Má řadu možných parametrů, některé z nich si nyní představíme. Jedna z variant tohoto příkazu má tvar INQUIRE (FILE=jmeno_souboru, seznam_parametru) a můžeme se její pomocí dotazovat na vlastnosti souboru s názvem jmeno souboru. Druhá varianta tohoto příkazu má tvar INQUIRE ([UNIT=]jednotka, seznam_parametru) a můžeme se její pomocí dotazovat na vlastnosti jednotky danou (přirozeným) číslemjednotka. Specifikátor UNIT= před číslem jednotky je opět volitelný. V seznamu parametrů dáváme za rovnítko proměnnou, do které se ukládá hodnota parametru, na který se dotazujeme. Parametrů je celá řada, některé z nich jsou analogické parametrům příkazu OPEN. Uved me některé parametry příkazu INQUIRE vhodné pro práci s textovými soubory: NAME=jmeno uloží do proměnné jmeno řetězec se jménem souboru. EXIST=existuje uloží do logické proměnné existuje hodnotu.true., pokud soubor existuje a může být otevřen nebo pokud uvedená jednotka existuje. Pokud soubor nebo jednotka neexistují, nebo soubor existuje, ale nemůže být otevřen, proměnné existuje bude přiřazena hodnota.false. OPENED=otevren přiřadí logické proměnné otevren hodnotu.true., pokud je uvedený soubor nebo jednotka připojena a hodnotu.false., pokud uvedený soubor nebo jednotka připojeny nejsou. ACTION=akce vrací v proměnné akce řetězec, který má hodnotu READ pokud je soubor otevřen pouze pro čtení, WRITE pokud je soubor otevřen pouze pro zápis a READWRITE pokud je soubor otevřen pro čtení i pro zápis. Hodnota UNDEFINED je přiřazena v případě, že soubor není připojen. NUMBER=cislo přiřadí do celočíselné proměnné cislo číslo jednotky právě připojené k souboru. Pokud není připojena k souboru žádná jednotka, přiřadí se hodnota 1. 45
10 Práce se soubory POSITION=pozice umožňuje dotaz na polohu v souboru. V proměnné pozice se vrací řetězec obsahující hodnotu REWIND pokud je soubor připojen a jeho poloha je na začátku souboru, APPEND pokud je soubor připojen a jeho poloha je na konci souboru (před značkou konce souboru pokud existuje), ASIS pokud je soubor připojen a jeho poloha se nemění, nebo UNDEFINED pokud soubor není připojen. READ=cteni, WRITE=psani, READWRITE=ctenipsani umožňují zjistit, zda ze souboru můžeme číst, zapisovast, nebo zároveň číst i zapisovat. V proměnných cteni, psani a ctenipsani bude vrácen řetězec obsahující YES pokud ano, NO pokud ne, a UNKNOWN pokud nelze odpověd zjistit. Jako příklad uvedeme dotaz na existenci souboru pokus.dat a dále zjistíme stav jednotky číslo 9, kterou jsme otevřeli v ukázce uvedené výše: CHARACTER(12) :: nm, ac, wr LOGICAL :: op, ex... INQUIRE(FILE= pokus.dat, EXIST=ex) IF (ex==.true.) THEN WRITE(*,*) Soubor pokus.dat existuje. ELSE WRITE(*,*) Soubor pokus.dat nelze najit. END IF... INQUIRE(UNIT=9, NAME=nm, ACTION=ac, OPENED=op, WRITE=wr) Úkoly 1. Vytvořte program, který do souboru data.txt napíše posloupnost celých čísel od 1 do 100. 2. Sestavte program, který do souboru pythagor.txt napíše vždy po třech do řádku Pythagorejská čísla a, b, c, tj. celá čísla vyhovující rovnici a 2 + b 2 = c 2. Pokud soubor pythagor.txt již existuje, přepište jej. 3. Upravte program pro řešení kvadratické rovnice tak, že hodnoty zadané z klávesnice i řešení rovnice budou (s podrobnějším popisem řešení) zapsány do souboru kvadrov.txt. Pokud soubor kvadrov.txt již existuje, připisujte výsledky na konec souboru. 4. Pomocí příkazu INQUIRE zjitěte, zda soubor data.txt existuje. Pokud ano, otevřete jej pouze pro čtení a zobrazte na obrazovce jeho obsah. Pokud neexistuje, vytvořte jej pouze pro zápis a zapište do něj všechny údaje, které dokážete zjistit o jeho stavu pomocí příkazu INQUIRE. 46
11 Pole II Pole ve Fortranu skýtají oproti ostatním jazykům velké možnosti. 11.1 Konstrukce polí Již víme, jak jedním příkazem naplnit celé pole, například INTEGER :: a(5) a = 1 Tímto příkazem nastavíme všechny prvky pole na jedničku. Pokud chceme do jednotlivých prvků přiřadit různé hodnoty, můžeme nejprve z těchto hodnot vytvořit pole a to přiřadit do pole a. Vytvoření pole ze zadaných hodnot se provede tak, že mezi (/ a /) vložíme jednotlivé hodnoty oddělené čárkami, například a = (/2,7,3,1,5/) Toho lze využít už při inicializaci pole v samotné deklaraci, např. (nezapomínat na dvojtečky za typem) REAL :: b(3) = (/1.0, 2.345, 34.0/) Tímto způsobem lze i spojovat pole dohromady, např. (pozor velikosti polí musí souhlasit) INTEGER :: a(3),b(2),c(5) a = (/1,2,3/); b = (/10,11/) c = (/a,b/)! výsledek c = (/1,2,3,10,11/) c = (/1,5,b,0/)! výsledek c = (/1,5,10,11,0/) V případě, že pole jsou velká, je vypisování všech hodnot obtížné. Pro tento případ je často možné při konstrukci pole využít cyklu. Tento cyklus má tvar podobný úplnému DO-cyklu: (vyraz,promenna=od,do,krok) Tento cyklus se vkládá mezi (/ a /) místo výčtu hodnot. Například: INTEGER :: a(5),i a=(/(i, i=1,5)/)!výsledek a=(/1,2,3,4,5/) a=(/(i, i=5,1,-1)/)!výsledek a=(/5,4,3,2,1/) a=(/(3*(i-1), i=1,5)/)!výsledek a=(/0,3,6,9,12/) a=(/(100*sin(10.*i), i=1,5)/)!výsledek a=(/-54,91,-98,74,-26/) a=(/0, (i,i=104,106), 1/)!výsledek a=(/0,104,105,106,1/) 47
11 Pole II Konstrukce (/../) vytváří nové pole, může se tedy vyskytovat nejen v přiřazovacím příkazu, ale kdekoliv, kde může být pole. Například a=-2*(/(i,i=1,5)/) CALL subr((/0,1,22/))!výsledek a=(/-2,-4,-6,-8,-10/) 11.2 Části pole Již jsme zmínili možnost práce s celými poli najednou. Další možností je práce jen s částí pole. Na místě indexu lze uvést dvě meze oddělené dvojtečkou. Získáme tím spojitý úsek pole začínající první mezí a končící druhou mezí např a(2:5). Kteroukoliv z mezí lze vynechat, potom se pracuje s úsekem od začátku resp. do konce pole, např. a(:5) nebo a(2:). Lze vynechat i obě meze. U jednorozměrného pole tím nic nezískáme, například a(:) je totéž co samotné a. Vynechání obou mezí u jednoho indexu je však velice časté u vícerozměrných polí, tím lze například z dvourozměrného pole udělat jednorozměrné. Například a(3,:) je jednorozměrné pole obsahující třetí řádek matice a, nebo a(:,j) je j-tý sloupec matice. Z pole můžeme vybrat i jednotlivé prvky, a to tak, že místo indexu uvedeme pole obsahující indexy vybíraných prvků. Toho lze využít i pro permutace prvků pole. Příklady: INTEGER :: a(4) = (/10,20,30,40/), c(4) INTEGER :: b(4) = (/4,2,1,3/) WRITE(*,*) a(2:3)! výsledek 20,30 WRITE(*,*) a(:2)! výsledek 10,20 WRITE(*,*) a(2:3)! výsledek 20,30 WRITE(*,*) a((/4,2,3/))! výsledek 40,20,30 c = a((/4,2,3,1/))! výsledek c = (/40,20,30,10/) c = a(b)! výsledek c = (/40,20,10,30/) c(b) = a! výsledek c = (/30,20,40,10/) 11.3 Standardní podprogramy pro pole Pro práci s poli existuje řada funkcí. Nejdůležitější z nich si nyní probereme. Funkce SUM a PRODUCT umožňují sečíst a vynásobit celé pole. SUM(array) Funkce sečte prvky pole typu INTEGER, REAL nebo COMPLEX PRODUCT(array) Funkce vynásobí prvky pole typu INTEGER, REAL nebo COMPLEX Funkce MAXVAL a MINVAL zjistí hodnotu největšího a nejmenšího prvku v poli. Často ale nestačí znát hodnotu největšího nebo nejmenšího prvku, ale i jeho polohu. K tomu slouží další dvě funkce MAXLOC a MINLOC 48
11 Pole II MAXVAL(array) Funkce vrátí hodnotu maximálního prvku pole typu INTEGER nebo REAL MINVAL(array) Funkce vrátí hodnotu minimálního prvku pole typu INTEGER nebo REAL MAXLOC(array) Funkce vrátí polohu maximálního prvku pole MINLOC(array) Funkce vrátí polohu minimálního prvku pole Poloha extrémního prvku se vrací jako celočíselné pole, jehož délka odpovídá počtu indexů vstupního. Pokud je vstupní pole například dvourozměrné dostaneme vektor o délce dvě. Pokud je vstupní pole jednorozměrné dostaneme vektor o délce jedna (pozor to je něco jiného než skalár). Tato nepříjemnost je již v jazyce Fortran95 odstraněna tím, že jako druhý parametr funkce MINLOC uvedeme číslo indexu, který nás zajímá a jako výsledek pak dostáváme skalár. Příklad: REAL :: a(4) = (/1.2, 3.6, -4.7, 2.2/), x INTEGER :: k,l(1) x = SUM(a)! výsledek x = 2.3 x = SUM(1+2*ABS(a))! výsledek x = 27.4 x = PRODUCT(a)! výsledek x = -44.6688 x = MAXVAL(a)! výsledek x = 3.6 l = MAXLOC(a)! výsledek l = (/2/) k = MAXLOC(a)!!! chyba - k není pole k = MAXLOC(a,1)! výsledek k = 2 Pro operace s maticemi a vektory jsou například funkce: DOT PRODUCT(a,b) skalární součin vektorů a a b oba vektory musí mít stejnou délku MATMUL(a,b) vynásobení matic a a b počet sloupců matice a se musí rovnat počtu řádků matice b TRANSPOSE(a) vrátí transponovanou matici a musí být dvojrozměrná matice RESHAPE(a,tvar) změna tvaru pole a pole jehož tvar se mění tvar jednorozměrný celočíselný vektor Za zmínku stojí především funkce RESHAPE. Tato funkce umožňuje převést např. jednorozměrné pole na dvojrozměrné nebo třeba matici 4 5 na 10 2. Počet prvků ve vstupním a výstupním poli by měl souhlasit. Tvar výsledného pole je popsán druhým parametrem funkce, např. (/2,3/) je matice 2 3, (/10/) je desetiprvkový vektor. Při transformaci se postupuje po sloupcích (první index se mění nejrychleji). Této funkce se nejčastěji používá při inicializaci vícerozměrného pole. Příklady: 49
11 Pole II INTEGER :: b(2,3)=reshape((/1,2,3,4,5,6/),(/2,3/))! 1 3 5! 2 4 6 INTEGER :: c(3,2)=reshape((/1,2,3,4,5,6/),(/3,2/))! 1 4! 2 5! 3 6 INTEGER :: l(2) c = RESHAPE(b,(/3,2/)) l = MAXLOC(b)! výsledek l = (/2,3/) 11.4 Dynamické alokace Pole, se kterými jsme se doposud setkali, měla dopředu známou velikost. Stejně jako v jiných programovacích jazycích (např. v jazyce C) existuje i ve Fortranu možnost dynamické alokace polí. Pokud chceme velikost pole určit až za běhu programu, uvedeme při jeho deklaraci specifikaci ALLOCATABLE a místo velikosti uvedeme pro každý rozměr jen dvojtečku. Samotná alokace paměti se provádí příkazem ALLOCATE. Jako parametr se uvede identifikátor pole s požadovanými rozměry. Pamět se uvolňuje příkazem DEALLOCATE. Zda bylo již pole alokováno lze zjistit funkcí ALLOCATED. INTEGER,ALLOCATABLE :: a(:) INTEGER :: n READ(*,*) n a(1)=3! chyba - pole ještě nebylo alokováno ALLOCATE(a(0:n)) a(n)=3... IF (ALLOCATED(a)) then DEALLOCATE(a) ALLOCATE(a(10)) ENDIF... DEALLOCATE(a) Úkoly 1. Naplňte pole A o 10 prvcích postupně hodnotami od 10 do 19 a pole B o 10 prvcích hodnotami sestupně od 50 do 41. Sečtěte obě pole a vytiskněte výsledek. 2. Naplňte jednorozměrné pole o 100 prvcích náhodnými čísly od 1 do 1000. Zdvojnásobte prvních 50 prvků pole. Vytvořte z pole matici typu 10 10. Zjistěte nejmenší a největší prvky pole a jejich umístění v poli. Zjistěte součet všech prvků a jejich průměrnou hodnotu. 50
11 Pole II 3. Načtěte z klávesnice celá čísla N a M. Ošetřete vstup tak, aby obě čísla byla v rozsahu od 1 do 6. Dynamicky alokujte matici A typu N M a vektor x o M prvcích. Načtěte do této matice i do vektoru hodnoty (z klávesnice). Vynásobte matici A (maticově) vektorem x a výsledek uložte do vektoru y (kolik prvků bude mít tento vektor?). Vytiskněte matici A a vektory x a y. Dealokujte matici a vektory. 51
11 Pole II 52
12 Grafika Grafika není standardní součástí jazyka. To, co si uvedeme v této kapitole platí pouze pro Visual Fortran v prostředí operačního systému Windows. Pokud chceme používat grafiku je třeba vybrat projekt s názvem Fortran Standard or QuickWin Application, a potom podtyp Standard Graphics. Vzniklý program bude vypadat velice podobně jako konzolová aplikace, kterou jsme používali doposud, pouze okno je větší a lze měnit jeho velikost. Abychom mohli používat grafické příkazy je třeba na začátku programu (hned za řádkem PROGRAM...) uvést příkaz USE DFLIB, nebo u starších kompilátorů USE MSFLIB. Pro grafiku existuje celá řada příkazů. Ukažme si několik základních. První skupina příkazů pracuje s celočíselnými souřadnicemi obrazovkových bodů (pixelů). První souřadnice (x) určuje pořadí bodu od levého okraje obrazovky. Druhá souřadnice (y) určuje pořadí bodu od horního okraje. Body se v obou směrech číslují od nuly. res = SETPIXEL(x,y) Nakreslí bod. INTEGER x,y souřadnice bodu INTEGER(2) res res = LINETO(x,y) Nakreslí čáru z aktuální pozice do zadaného bodu. INTEGER x,y souřadnice koncového bodu INTEGER(2) res CALL MOVETO(x,y,xy) Nastaví aktuální pozici do zadaného bodu. INTEGER x,y souřadnice bodu TYPE(xycoord) xy proměnné uživatelského typu (viz. kapitola xx), pro nás není důležitá Návratové hodnoty některých funkcí (res) nejsou pro nás důležité. Nyní si již můžeme ukázat jednoduchý grafický program nakreslení čtverce s bodem uprostřed. PROGRAM ctverec USE dflib IMPLICIT NONE INTEGER :: sx,sy,l INTEGER :: r TYPE(xycoord) cxy sx = 200 sy = 100 l = 50! souřadnice středu! souřadnice středu! polovina délky strany 53
12 Grafika r = setpixel(sx,sy)! bod do středu CALL moveto(sx+l, sy+l, cxy)! čtverec r = lineto(sx-l, sy+l) r = lineto(sx-l, sy-l) r = lineto(sx+l, sy-l) r = lineto(sx+l, sy+l) READ(*,*) END PROGRAM Rozlišení obrazovky v bodech lze zjistit pomocí příkazu GETWINDOWCONFIG, viz následující kód: TYPE (windowconfig) msc INTEGER :: res res=getwindowconfig(msc) WRITE(*,*) šířka obrazovky :,msc%numxpixels WRITE(*,*) výška obrazovky :,msc%numypixels Předchozí příkazy pracovaly s jednotlivými pixely. Při kreslení například grafu funkce však většinou chceme pracovat s reálnými souřadnicemi o zadaném rozsahu. I toto Digital Fortran umožňuje. Předchozí příkazy mají ještě tvar, kdy se souřadnice zadávají jako reálná čísla: res = SETPIXEL W(x,y) Nakreslí bod. REAL(8) x,y souřadnice bodu INTEGER(2) res res = LINETO W(x,y) Nakreslí čáru z aktuální pozice do zadaného bodu. REAL(8) x,y souřadnice koncového bodu INTEGER(2) res CALL MOVETO W(x,y,xy) Nastaví aktuální pozici do zadaného bodu. REAL(8) x,y souřadnice bodu TYPE(wxycoord) xy proměnné uživatelského typu (viz. kapitola xx), pro nás není důležitá Jak je vidět názvy se liší přidaným W na konci. Souřadnice jsou reálné a ne celočíselné. Pozor na změnu typu u MOVETO. Souřadnice bodů nemají u těchto příkazů vztah k obrazovkovým bodům, ale jejich rozsah je třeba nastavit. Pro nastavení rozsahu je příkaz res = SETWINDOW(dir,x0,y0,x1,y1) Nastaví rozsah reálných souřadnic. REAL(8) x0,y0 souřadnice levého dolního rohu REAL(8) x1,y1 souřadnice pravého horního rohu 54
12 Grafika LOGICAL dir směr osy y,.true. zezdola nahoru,.false. zezhora dolu INTEGER(2) res Následující ukázkový program nakreslí graf funkce sinus. PROGRAM sinusovka USE dflib IMPLICIT NONE TYPE(wxycoord) cxy INTEGER :: r, i, n = 100 REAL :: x, xkon = 4*3.1415926 r = setwindow(.true., 0.0, -1.0, xkon, 1.0) CALL moveto_w(0.0_8,0.0_8,cxy) DO i=1,n x = xkon/n*i r = lineto_w(x,sin(x)) ENDDO READ(*,*) END PROGRAM At už pracujeme se souřadnicemi pixelů či s reálnými souřadnicemi, řada funkcí je společná. Ty nejpoužívanější jsou: CALL CLEARSCREEN ($GCLEARSCREEN) Vymaže obrazovku. res = SETCOLOR(c) Nastaví barvu číslo c. INTEGER(2) c číslo barvy (0 černá,..., 15 bílá) INTEGER(2) res res = SETCOLORRGB(c) Nastaví barvu RGB. INTEGER(4) c libovolná barva INTEGER(2) res Mimo tří základních funkcí které jsme si uvedli existuje řada dalších: res = RECTANGLE(typ,x0,y0,x1,y1) Nakreslí obdélník. INTEGER x0,y0 levý dolní roh INTEGER x1,y1 pravý horní roh INTEGER typ $GBORDER nakreslí jen obvod, $GFILLINTERIOR vyplněný obdélník res = ELLIPSE(typ,x0,y0,x1,y1) Nakreslí elipsu. INTEGER x0,y0 levý dolní roh ohraničujícího obdélníku INTEGER x1,y1 pravý horní roh ohraničujícího obdélníku INTEGER typ jako u obdélníku 55
12 Grafika res = FLOODFILL(x,y,bc) Vyplní oblast barvou. INTEGER x,y bod uvnitř oblasti INTEGER bc číslo barvy hranice c = GETPIXEL(x,y) Zjistí barvu pixelu. INTEGER x,y souřadnice bodu INTEGER c číslo barvy CALL OUTGTEXT(str) Vypíše řetězec na aktuální pozici. CHARACTER(*) str vypisovaný řetězec Před použitím této funkce je třeba volat příkazy INITIALIZEFONTS a SETFONT res = INITIALIZEFONTS() Inicializuje grafické písmo. res = SETFONT(str) Vybere grafický font pro OUTGTEXT. CHARACTER(*) str řetězec popisující požadovaný font, např. t Arial h16w7 viz nápověda. Ukázky použití uvedených příkazů můžete najít v programu GR DEMO.F90. Uvedené příkazy mají i varianty pracující s reálnými souřadnicemi. Úkoly 1. Napište program, který zobrazí funkci y = cos x v intervalu 2π, 2π včetně souřadnicových os, značek a popisků. 2. Napište program, který bude na obrazovku na náhodná místa zobrazovat náhodné geometrické tvary vyplněné náhodnými barvami. 56
13 Moduly Jazyk Fortran umožňuje rozdělit program do menších celků tzv. modulů. Moduly by měly být pokud možno samostatné a vícenásobně použitelné jednotky. Při tvorbě většího programu bychom se měli snažit rozdělit program do samostatných celků. Tento tzv. modulární přístup má řadu výhod. Modul lze ladit a zkoušet postupně, lokalizace chyb je potom jednodušší. Hlavní výhodou ale je, že při vhodném návrhu lze moduly používat vícekrát. Příkladem modulu, se kterým jsme se již setkali, je modul pro práci s grafikou. Řadu užitečných modulů lze získat i z internetu modul pro práci s libovolně přesnými reálnými čísly, modul pro tvorbu postscriptových souborů. Jednotlivé moduly je vhodné umist ovat do samostatných souborů, opět s koncovkou.f90. Modul je velice podobný programu, ale s několika rozdíly: Začíná MODULE místo PROGRAM. Končí END MODULE místo END PROGRAM. Nesmí obsahovat žádné výkonné příkazy mezi deklaracemi a CONTAINS. Do modulu můžeme umist ovat podprogramy nebo proměnné. Tyto můžeme používat v hlavním programu nebo i v jiném modulu. Pokud chceme modul použít, je třeba bezprostředně za prvním řádkem (PROGRAM... nebo MODULE...) uvést USE a název modulu. Příklad modul s funkcí na výpočet mocniny. Soubor mod mocnina.f90 MODULE mod_mocnina IMPLICIT NONE INTEGER :: exponent CONTAINS FUNCTION f(x) REAL :: f,x INTEGER :: I f=x DO i=2,exponent f=f*x END DO END FUNCTION END MODULE Soubor test mocnina.f90 PROGRAM test_mocnina 57
13 Moduly USE mod_mocnina IMPLICIT NONE exponent = 4 WRITE(*,*) f(1.24) END PROGRAM! chceme používat modul! nastavíme velikost exponentu v modulu! zavolání funkce z modulu Všechny proměnné a podprogramy z modulu lze používat v libovolné nadřazené jednotce (modulu či programu). Naopak ale proměnné a podprogramy z nadřazené jednotky nelze v modulu používat. Tj. Proměnné jsou vidět pouze zezdola nahoru. Pokud uvnitř modulu chceme mít proměnné (podprogramy), které nejde zvenčí použít, můžeme pro vybranou proměnnou použít specifikaci PRIVATE. Naopak pro proměnné přístupné zvenčí se používá specifikace PUBLIC. Pokud před deklarací proměnných uvedeme samotné PRIVATE nebo PUBLIC vztahuje se na všechny následující deklarace. Úkoly 1. Napište modul, který bude definovat hodnoty globálních reálných proměnných a, b, c pro výpočet kořenů kvadratické rovnice ax 2 + bx + c = 0. Použijte tento modul v programu na řešení kvadratické rovnice (viz kapitola 5.1). 2. Napište modul, ve kterém budou zahrnuty funkce vytvořené pro změnu velkých písmen v řetězci na malá, změnu malých písmen v řetězci na velká a vymazání všech mezer z řetězce (viz kapitola 9). Tyto funkce použijte v programu, který bude tento modul používat. 58
14 Parametry podprogramů 14.1 Podprogramy jako parametry Někdy bývá potřeba předat podprogramu odkaz na jiný podprogram. Příkladem může být podprogram pro vykreslení grafu funkce, kdy je třeba určit graf které funkce chceme vykreslovat. Fortran toto předávání podprogramů jako parametrů umožňuje. V hlavičce podprogramu uvedeme název podprogramu, který chceme používat uvnitř podprogramu. Místo typu tohoto identifikátoru, jako u ostatních proměnných, uvedeme mezi INTERFACE a END INTERFACE zkrácený popis funkce. Uvádíme pouze první a poslední řádek podprogramu a specifikaci předávaných parametrů. PROGRAM fce_par IMPLICIT NONE WRITE(*,*) maximum(-1.0,6.0,f2) CONTAINS!=============================== FUNCTION maximum(a,b,f) REAL :: a,b,maximum INTERFACE FUNCTION f(x) REAL :: f,x END FUNCTION END INTERFACE INTEGER :: i...! viz přilozený program END FUNCTION!=============================== FUNCTION f2(x) REAL :: f2,x f2 = 3-(x-2)**2 END FUNCTION!=============================== END PROGRAM 14.2 Volitelné parametry Často se setkáváme s procedurami které mohou mít různý počet parametrů (např. subroutina WRITE). Takové subroutiny můžeme deklarovat sami. U parametrů, které nemusí být vždy přítomné (nepovinné parametry), uvádíme specifikaci OPTIONAL. Povinné parametry musí vždy v hlavičce vždy před nepovinnými. Pokud při volání podprogramu uvedeme méně parametrů, 59
14 Parametry podprogramů dostane hodnotu pouze odpovídající počet prvních nepovinných parametrů. Pokud chceme zadat nepovinný parametr, který není na uveden na začátku, lze ho při volání podprogramu zadat ve tvaru identifikátor proměnné = hodnota. Pokud uvnitř podprogramu chceme zjistit zda byl volitelný parametr zadán, použijeme funkci PRESENT(prom). Tato funkce vrací.true. pokud byl parametr zadán. Příklad: PROGRAM vol_par IMPLICIT NONE CALL sub(1,2) CALL sub(1,2,3) CALL sub(1,2,d=4) CALL sub(a=2,c=4)! chyba - chybí b CONTAINS!=============================== SUBROUTINE sub(a,b,c,d) INTEGER :: a,b INTEGER, OPTIONAL :: c,d WRITE(*, ( a=,i3) ) a WRITE(*, ( b=,i3) ) b IF (PRESENT(c)) WRITE(*, ( c=,i3) ) c IF (PRESENT(d)) WRITE(*, ( d=,i3) ) d END SUBROUTINE!=============================== END PROGRAM Úkoly 1. Vytvořte modul s několika různými funkcemi (např. f 1 = cos 2 x, f 2 = sin 2x, apod.). Načtěte z klávesnice proměnnou x a pomocí menu vyberte funkci, jejíž hodnotu v daném bodě spočítáte. 2. Upravte program pro kreslení grafu funkce kosinus (viz kapitola 12) tak, že pomocí menu v úvodu programu bude možné si vybrat funkci, kterou na grafu zobrazíte. Využijte modul vytvořený v předchozím úkolu. 3. Napište podprogram, který bude místo mezer v řetězci doplňovat jiné znaky. Znak, který se bude místo mezer zapisovat, pošlete jako volitelný parametr podprogramu. Pokud nebude volitelný parametr zadán, doplňte řetězec nulami. Tento podprogram otestujte v hlavním programu. 60
15 Uživatelský typ Mimo pěti standardních typů (INTEGER, REAL, COMPLEX, CHARACTER, LOGICAL) existuje další uživatelsky definovaný typ. Předtím, než definujeme proměnné tohoto typu, je třeba definovat samotný typ. Definice typu začíná TYPE nazev a končí END TYPE. Mezi těmito řádky definujeme jednotlivé složky typu, stejně jako bychom deklarovali obyčejné proměnné. Například TYPE datum INTEGER :: den,mesic,rok END TYPE TYPE osoba CHARACTER(30) :: jmeno LOGICAL :: muz TYPE(datum) :: narozen REAL :: plat END TYPE Pokud chceme deklarovat proměnnou uživatelského typu, jako typ uvádíme TYPE(nazev typu). Například TYPE(datum) vcera, zitra TYPE(osoba) ja, vedouci, uklizecka Odkazy na jednotlivé položky se uvádějí pomocí znaku procento %. Například vcera%rok = 2002 ja%jmeno = Müller Thürgau ja%narozen%rok = 1212 Řada překladačů umožňuje používat místo znaku procento tečku (ja.narozen.rok = 1348). Tato možnost ale není součástí normy a některé překladače ji nepodporují. Pro zachování přenositelnosti zdrojového kódu je tedy lepší používat %. Stejně jako u ostatních typů i uživatelský typ můžeme použít pro sestavení pole: TYPE(datum) svatky(20) Na jednotlivé prvky tohoto pole odkazujeme podobně: svatky(1)%rok = 2004 svatky(1)%mesic = 1 svatky(1)%den = 1 61
15 Uživatelský typ Pro pohodlnější zadávání hodnot můžeme využít konstruktoru, který je představován názvem typu a uvnitř kulatých závorek jsou jednotlivé hodnoty (v počtu a v pořadí ve shodě s definicí uživatelského typu): svatky(2) = datum(2004, 5, 1) Pokud chceme uživatelský typ vypsat na obrazovku, můžeme vypsat bud jeho složky, nebo najednou celý záznam, např. PRINT *, svatky(1)%rok, svatky(1)%mesic, svatky(1)%den PRINT *, svatky(1)! Totéž co předchozí řádek S uživatelskými typy jsme se již setkali při práci s grafikou typy xycoord a windowconfig. Například typ xycoord slouží pro uchování souřadnic x a y na obrazovce a je definován takto: TYPE xycoord INTEGER(2) xcoord INTEGER(2) ycoord END TYPE xycoord! souřadnice x! souřadnice y Úkoly 1. Definujte vlastní typ kniha, který bude obsahovat záznamy o knihovně: evidenční číslo, autor, název, nakladatel, rok vydání, počet výtisků a zda je kniha zapůjčena (logická proměnná). Naplňte tento seznam hodnotami a zapište je do souboru. 2. Upravte předchozí program tak, že se při dalším volání programu seznam načtete ze souboru a pak se připisují další záznamy. 3. Napište program, který předchozí seznam knih načte ze souboru, seřadí knihy abecedně podle autora a setříděný seznam opět uloží do souboru. Využijte funkce pro lexikální porovnávání řetězců: LGT, LLT popř. LGE, LLE. 62
16 Fortran 77 Fortran 77 je předcházející verze jazyka Fortran. Z dnešního hlediska se jedná již o nemoderní jazyk, který postrádá řadu potřebných rysů (dynamické alokace, globální proměnné, modulární struktura) a pro dnešní použití ho nelze doporučit. V jazyce Fortran 77 je však napsáno obrovské množství programů, zvláště v matematických a fyzikálních aplikacích. Fortran 77 je naštěstí podmnožina Fortranu 90. Kompilátory F90 by proto měly být schopny překládat i jazyk F77. Je tedy třeba pasivně rozumět jazyku F77 a je třeba umět připojit zdrojový kód v F77 k programu F90. Pro připojení stačí uložit kód s koncovkou.for a vložit do projektu stejně jako kód ve F90. První rozdílem mezi F90 a F77 je formální vzhled zdrojového kódu. Jazyk F77 byl původně zapisován na děrné štítky, na každý štítek jeden řádek programu. Z toho dodnes vyplývá následující formát řádku. Sloupce 1 až 5 jsou využívány pro návěští. Pokud je v prvním sloupci některý ze znaků *, C, c, je celý řádek komentář. Sloupec 6 se normálně nepoužívá. Pokud je v tomto sloupci jakýkoliv znak, je tento řádek pokračováním řádku předchozího. Do sloupců 7 až 72 se zapisují vlastní příkazy. Na každém řádku může být pouze jeden příkaz. Prostředí MSDS podporuje psaní v tomto formátu viz obr. 5. Ve Fortranu 77 najdeme několik příkazů a rysů, které jsou již považovány za zastaralé a není doporučeno je nadále používat. Z programátorského hlediska je třeba zavrhnout příkaz GOTO ve všech svých variantách, který umožňuje nepodmíněné skoky na jiná místa v programu označená návěštím. Fortran 90 již zcela podporuje strukturování programu a skokům na jiná místa pomocí GOTO se lze vždy vyhnout. Bohužel, tento příkaz byl dříve používán velice často, což vedlo ke značné nepřehlednosti řady programů napsaných ve Fortranu 77. Dalším příkladem jsou bloky COMMON, které slouží pro ukládání dat a sdílení globálních proměnných. Pro globální proměnné je dnes daleko lepší použít modul, ve kterém jsou společně umístěny. Podrobně popisuje Fortran 77 kniha [1], kde můžeme nalézt ještě několik dalších příkazů, jejichž použití dnes již není nutné. Jelikož Fortran 77 je podmnožinou Fortranu 90, nalezneme tyto příkazy i v nápovědě a dokumentaci kompilátorů Fortran 90. 63
16 Fortran 77 Obrázek 5: Kód ve Fortranu 77 64