FORTRAN

Język FORTRAN pochodzi z czasów kiedy programy były pisanie na arkuszach papieru, następnie przepisywane na karty dziurkowane lub i w takiej formie analizowane przez komputer. Kompilator wyrzucał kompilat w postaci taśmy dziurkowanej, którą można było już ‘wpuścić’ w maszynę i po chwili otrzymać wyniki na przypominającej młockarnię – drukarce wierszowej.

Ponieważ objętość arkuszy programu a szczególnie kart perforowanych – była ograniczona –zapisu programu musiał spełniać ustalone reguły.

Po pierwsze program składa się z linii w których zapisane są – jedna deklaracja lub instrukcja w jednej linii – ewentualnie kontynuowanej w kolejnych liniach – ale bez możliwości umieszczania kilku instrukcji obok siebie w jednej linii.

W języku FORTRAN program jest wykonywany instrukcja po instrukcji – tak jak są one zapisane w tekście programu. Możliwe są tu skoki – i co może się wydać dziwne dzisiejszym programistom przyzwyczajonym do programowania strukturalnego – nie są one niczym wstydliwym. Skoki i końce pętli musimy oznaczyć w programie wskazując w jakiej linii kończy się pętla, czy też do jakiej linii kodu powinien program przeskoczyć. Linie takie oznaczamy etykietą – liczbą całkowitą z przedziału od 1 do 99999. Liczby te nie muszą mieć nic wspólnego z kolejnością linii i nie muszą występować na początku linii (jak w starych wersjach języka BASIC). Wstawiamy je tam gdzie są potrzebne, i nadajemy im takie wartości jakie przyjdą nam do głowy.

Każda linia ma określony format – miejsca zajmowane przez kolejne znaki mają różne przeznaczenie:

Etykieta może wystąpić jedynie w linii która jest początkiem instrukcji (nie może wystąpić w linii będącej kontynuacją poprzedniej linii). Jeśli w pierwszej linii znajduje się litera 'C' lub '*' - linia ta zawiera komentarz. Komentarze nie mogą być kontynuowane w następnej linii - można natomiast umieścić w następnej linii kolejny komentarz. Komentarze są pomijane przez kompilator podobnie jak spacje które nie mają żadnego znaczenia jeśli nie są częścią stałych tekstowych.

Program w języku FORTRAN podzielony jest na bloki. Kolejność występowania bloków nie jest istotna. Możemy w blokach korzystać z bloków które są zadeklarowane później – a nie tak jak w C – tylko tych które już zadeklarowano.

Bloki mają podobną konstrukcję[1]:

 

DEKLARATOR_BLOKU nazwa(lista parametrów)

    deklaracje zmiennych

    zawartość bloku (wiele linii kodu)

END

 

Do dyspozycji mamy 4 rodzaje bloków:

·         PROGRAM – blok od którego rozpoczyna się wykonywanie programu. Gdy instrukcje zapisane w tym bloku zakończą swoje działanie – program się kończy. W programie może być tylko jeden blok PROGRAM.

·         SUBROTINE – podprogram – coś w rodzaju procedury. Fragment programu który wykonuje jakieś czynności.

·         FUNCTION – funkcja – podobnie jak podprogram – jednak zwraca wartość – jak to funkcja.

·         BLOCK DATA – blok danych – blok pozwalający na zainicjowanie wspólnych zmiennych w programie.

Bloki nie mogą być zagnieżdżone – podobnie jak funkcje w języku C. Bloki mogą się nawzajem wywoływać – i nie ma limitu na ilość wywołań – poza oczywiście rozmiarem stosu maszynowego naszego komputera. Funkcje wywołujemy podając ich nazwy i w nawiasach – parametry natomiast procedury wywołuje się pisząc instrukcję CALL, nazwę procedury i w nawiasach – parametry.

Blok PROGRAM

Blok program jest blokiem od którego rozpoczyna się wykonywanie programu. W bloku tym możemy podać nazwę programu, ale ma ona znaczenie komentarza. Nie ma sensu podawanie parametrów. Mogą być one używane w nowszych mutacjach tego języka. Dla nas parametry bloku PROGRAM nie mają znaczenia.

Blok FUNCTION

Blok definicji funkcji musi posiadać nazwę. Nazwa bloku jest jednocześnie nazwą funkcji. Jeśli funkcja posiada parametry – ją one wymienione w nawiasach po nazwie bloku. Parametry oddzielany przecinkami. Funkcję  wywołujemy podając po prostu jej nazwę oraz wartości parametrów  w nawiasach. Na liście parametrów podajemy jedynie nazwy poszczególnych parametrów. Ich typu – podobnie jak typ zwracanego wyniku – deklarujemy w części bloku przeznaczonej na deklaracje. Przypomina to trochę stary język C. Liczba i typy parametrów muszą się zgadzać z tymi jakie są zadeklarowane w funkcji.

Typ zwracanego wyniku określmy deklarując typ zmiennej o nazwie takiej samej jak nazwa funkcji. Trochę to skomplikowane, ale można się przyzwyczaić. Na przykład funkcję fi2r która przyjmuje jako parametr liczbę całkowitą i dwie liczby rzeczywiste a zwraca – liczbę zespoloną możemy zadeklarować jako:

 

FUNCTION fi2r(a,b,c)

  INTEGER a

  REAL b,c

  COMPLEX fi2r

 

  ...

 

END

 

Jeśli funkcja nie wymaga podania parametrów (jest bezargumentowa) - listę parametrów oraz nawiasy można pominąć

Wartość zwracaną przypisujemy do zmiennej o takiej samej nazwie jak nazwa funkcji. Nazwa ta może pojawić się jedynie po lewej stronie znaku równości.

Jeśli jako parametr przy wywoływaniu funkcji podamy nazwę zmiennej - to zostanie ona przekazana przez referencję i będzie można zmienić jej wartość z wnętrza funkcji. Jeśli przy wywoływaniu funkcji, w miejscu parametry, wpiszemy wyrażenie - zostanie ono przekazane przez wartość. Może to być trochę mylące, ale pozwala na znaczne przyspieszenie wykonywania programu. Ciekawe jest to że do tej samej funkcji te same parametry mogą trafiać raz przez wartość a raz przez referencje.

Blok SUBROUTINE

Blok definicji podprogramu wygląda podobnie jak blok definicji funkcji, nie ma tu jednak sensu definiowanie typu ani przypisywanie na nazwę procedury. Podobnie ja w funkcji - parametry przekazywane są przez referencję - o ile to jest możliwe. Jeśli nie – przekazywane są przez wartość.

Zmienne proste

Zmienne identyfikowane są przez ich nazwy. Nazwa zmiennej składa się z ciągu znaków (liter i cyfr) z których pierwszy jest literą. Długość nazwy zmiennej nie może przekraczać 6 znaków. W FORTRANIE występują następujące typy zmiennych:

·         Całkowite (INTEGER)

·         Rzeczywiste (REAL)

·         Rzeczywiste o zwiększonej precyzji (DOUBLE PRECISION)

·         Zespolone (COMPLEX)

·         Logiczne (LOGICAL) - przyjmujące jedną z dwu wartości: .TRUE. i .FALSE.

·         Znakowe (CHARACTER) - pozwalające na przechowywanie tekstu o zadanej długości. Długość tekstu podaje się w nawiasach, lub pisząc znak '*' oraz liczbę znaków po deklarowanej zmiennej.

Zmienne zajmują zawsze określoną ilość miejsca w pamięci komputera. Zmienne typu INTEGER, REAL oraz LOGICAL zajmują jedną jednostkę pamięci, Zmienne typu DOUBLE PRECISION oraz COMPLEX - dwie takie jednostki. Zmienne typu CHARACTER – liczone są w innych jednostkach – nie należy więc ich mieszać z typami liczbowymi i nie jest bezpiecznie (czytaj – mądrze – używać instrukcji EQUIVALENCE do mieszania typów znakowych i liczbowych.

Zmienne proste nie muszą być deklarowane. Jeśli zmienna nie jest zadeklarowana - jej typ jest określony przez pierwszą literę nazwy tej zmiennej. Jeśli zmienne jest zadeklarowana - jej typ określa jej deklaracja.

Tablice

Tablice muszą być zadeklarowane przed ich użyciem. Deklaracja może być określeniem typu tablicy. Wygląda to wtedy jak deklarowanie typu zmiennej prostej, jednak po nazwie zmiennej podaje się w nawiasach okrągłych rozmiar (rozmiary - dla tablicy wielowymiarowej) tablicy. Rozmiar możemy podać pojedynczą liczbą - indeks tablicy przebiega wtedy wartości od 1 to zadanej wartości włącznie. Można także określić jawnie zakres zmiany indeksu podając dwie liczby oddzielone dwukropkiem - min:max.

Tablicę można również definiować używając deklamatora DIMENSION, lub wymieniając go na liście instrukcji COMMON. W obu tych przypadkach typ jest zależny od pierwszej litery nazwy tablicy.

Tablice mogą mieć maksymalnie 6 wymiarów.

Inne typy

W programie napisanym w języku FORTRAN nie ma tu możliwości definiowania struktur ani tablic których elementami są tablice. Nie ma także możliwości zadeklarowania tablic o rozmiarze dynamicznie wyznaczanym przez program.

Przypisania

Instrukcje przypisania mają postać zmienna=wyrażenie. Jest to naturalna i prosta forma zapisu. Najpierw wartościowana jest prawa strona wyrażenia, następnie następuje przypisanie wartości do zmiennej. Prawidłowe są wiec konstrukcje I=I+1.

Przypisania wykonywane na wycinek tekstu (fragment zmiennej tekstowej) dotyczą zawsze tylu znaków ile zawiera wycinek. W razie potrzeby zawartość wycinka uzupełniana jest spacjami lub przycinana.

Inną formą przypisania jest instrukcja ASSIGN o formacie ASSIGN etykieta TO zmienna, gdzie zmienna jest zmienną całkowitą, natomiast etykieta jest etykietą z bieżącego segmentu. Użycie tej instrukcji pozwana na wykonywanie skoków do etykiet zapamiętywanych w zmiennych. Nie jest to przypisanie wartości liczbowej – wartość takiej zmiennej nie jest równa liczbie z etykiety.

Operatory arytmetyczne

W języku FORTRAN dostępne są podstawowe dwuargumentowe operatory arytmetyczne:

·         + - dodawanie

·         - - odejmowanie

·         - mnożenie

·         / - dzielenie - wszystkie cztery - lewostronnie łączne)

·         ** - potęgowanie - działanie prawostronnie łączne

Typ wyniku zależy od typu argumentów

1.        Jeśli argumenty są takiego samego typu - to wynik jest tego samego typu co argumenty (UWAGA: dzielenie wartości całkowitych jest dzieleniem całkowitym).

2.        Jeśli argumenty mają różny typ - wykonuje się konwersję jednego z nich do typu drugiego zgodnie z zasadami:

o        Mieszanie typów zespolonego i podwójnej precyzji jest zabronione

o        Jeśli jeden z argumentów jest typu zespolonego lub podwójnej precyzji to drugi argument otrzymuje ten sam typ.

o        Jeśli jeden argument jest typu rzeczywistego a drugi całkowitego - to przekształca się wartość całkowitą.

Konstruując wyrażenia można używać nawiasów w celu zmiany priorytetów działań w wyrażeniu. Jeśli nie użyto nawiasów, to priorytety są następujące:

1.        wywołania funkcji,

2.        potęgowanie,

3.        mnożenie i dzielenie,

4.        dodawanie i odejmowanie

Operatory logiczne

Wyrażenia logiczne można konstruować używając operatorów logicznych oraz operatorów relacji. Operatory logiczne operują na argumentach logicznych, Operatory relacji pozwalają na wartościowanie logiczne warunków nakładanych na wartości wyrażeń arytmetycznych i znakowych. Operatory logiczne oraz operatory relacji wyglądają nieco dziwnie ale używa się ich w taki sam sposób jak operatorów arytmetycznych. Do dyspozycji mamy:

·         .NOT. - operator negacji (jednoargumentowy)

·         .AND. - iloczyn logiczny

·         .OR. - suma logiczna

·         .EQV. - równoważność

·         .NEQV. - nierównoważność

·         .GT. - relacja większości (ang. Greater Than, '>' w C)

·         .GE. - relacja nie mniejszości (ang Greater or Equal, '>=' w C)

·         .LT. - relacja mniejszości (ang Less Than, '<' w C)

·         .LE. - relacja nie większości (ang Less or Equal, '<=' w C)

·         .EQ. - relacja równości (ang EQual, '==' w C)

·         .NE. - relacja nierówności (ang Not Equal, '!=' w C)

Operatory znakowe

W Języku FORTRAN jest tylko jeden operator znakowy - operator łączenia (konkatenacji) napisów - // . Inną operacją która nie jest określona żadnym operatorem jest operacja brania wycinka tekstu. Operację tą zapisuje się jako zmienna(od:do), gdzie zmienna jest nazwą zmiennej znakowej, a wartości od i do są wyrażeniami całkowitymi spełniającymi warunek: 1<=od<=do<=dlugość tekstu

Wywołania funkcji

Wywołanie funkcji ma postać nazwafunkcji(argumenty), gdzie nazwa funkcji jest nazwą funkcji wbudowanej lub zadeklarowanej przez programistę, natomiast argumenty funkcji są listą wyrażeń oddzielonych przecinkami, których liczba oraz typy musza być zgodne z ilością i typami parametrów jakie są zadeklarowane w definicji tej funkcji.

Instrukcja skoku: GOTO

Instrukcja GOTO zmienia kolejność wykonywania instrukcji w programie - kolejną instrukcją które będzie wykonana, będzie instrukcja umieszczona w linii opatrzonej wskazaną etykietą. Instrukcja GOTO występuje w trzech postaciach:

·         bezwarunkowe GOTO powoduje natychmiastowy skok do wskazanej instrukcji: GOTO n

·         wyliczane GOTO. Instrukcja o formacie: GOTO (n1,n2,...,nn),w , gdzie n1..nn są etykietami natomiast w jest wyrażeniem całkowitym. W zależności od wartości wyrażenia - wykonywany jest skok do etykiety zajmującej w-tą pozycję na liście etykiet. Jeśli wartość wyrażenia w jest mniejsza od 0 lub większa od liczby etykiet na liście - skok nie jest wykonywany.

·         przypisane GOTO: GOTO v lub GOTO v,(n1,n2,...,nn) - powoduje skok do instrukcji której etykieta znajduje się w zmiennej v. Nie jest to jednak numer odpowiadający etykiecie, ale specjalna wartość jaką można nadać zmiennej całkowitej używając instrukcji ASSIGN, wyliczającej adres (w pamięci) pod jakim znajduje się instrukcja opatrzona wybraną etykietą. W drugiej wersji - na liście umieszczamy dozwolone etykiety pod które może być wykonany skok.

Wbrew temu czego uczymy się poznając języki w których programujemy strukturalnie – instrukcję GOTO można i należy używać wszędzie tam gdzie jest potrzebna.

Instrukcja warunkowa IF

Instrukcja IF pozwala na wykonywanie pewnych fragmentów programu w zależności od prawdziwości pewnych wyrażeń logicznych. W języku FORTRAN, instrukcja warunkowa występuje w trzech mutacjach:

·         Logiczne IF: Instrukcja ma format IF (warunek) instrukcja, gdzie warunek jest jakimkolwiek wyrażeniem typu logicznego, natomiast instrukcja jest dowolną instrukcją języka FORTRAN za wyjątkiem instrukcji pętli DO, i innych instrukcji warunkowych

·         Arytmetyczne IF o formacie: IF (wyrażenie) etykieta1, etukieta2, etykieta3. Wykonanie tej instrukcji polega na wykonaniu skoku do instrukcji opatrzonej etykietą etykieta1, jeśli wartość wyrażenia jest mniejsza od zera, etykieta2, jeśli wartość wyrażenia jest równa 0, lub etykieta3, jeśli wartość wyrażenia jest dodatnia. Wyrażenie może być dowolnym wyrażeniem całkowitym, rzeczywistym, lub rzeczywistym podwójnej precyzji.

·         Blokowe IF - podobne do instrukcji IF znanej z języków PASCAL, C czy C++. Blok instrukcji wykonywanych warunkowo rozpoczyna się instrukcją IF (warunek) THEN , zaś kończy instrukcją END IF. Wewnątrz bloku może znajdować się dowolnie dużo instrukcji. Dodatkowo, można zaznaczyć blok instrukcji wykonywanych gdy warunek nie jest spełniony (pomiędzy IF ... THEN a END IF umieszczamy instrukcję ELSE, lub ELSE IF (warunek) THEN - jeśli potrzebujemy bardziej skomplikowanych konstrukcji warunkowych). Wewnątrz dowolnego bloku może wystąpić blokowa instrukcja warunkowa (zagnieżdżona). Pętle muszą zawierać się całkowicie w bloku warunkowym. Nie można skoczyć do wnętrza bloku warunkowego.

Instrukcja pętli: DO

Instrukcja DO służy do zorganizowania pętli w programie. Instrukcja ma postać DO etykieta zm=w1,w2,w3, gdzie etykieta, jest etykietą ostatniej instrukcji wykonywanej w pętli i musi znajdować się poniżej instrukcji DO. Zmienna zm, jest zmienna sterującą pętli. Na początku pętli nadawana jest jej wartość równa wartości wyrażenia w1, i po każdym obiegu pętli dodawana jest wartość wyrażenia w3. Instrukcje w pętli powtarzane są do chwili, kiedy zmienna zm będzie miała wartość większą lib równa wyrażeniu w2. Wyrażenie w3 może być pominięte, i wtedy krok pętli wynosi 1.

Wartości wyrażeń w2 i w3 są wyliczane tylko raz na początku pętli i na ich podstawie wyznaczana jest ilość obiegów pętli DO. Zmiana składników wyrażeń wewnątrz pętli nie wpłynie w żaden sposób na ilość iteracji pętli. Podobnie nie ma sensu zmieniać w pętli wartości zmiennej sterującej. Nie będzie to miało żadnego znaczenia (niektóre kompilatory wykażą w takiej sytuacji błąd, lub wypiszą ostrzeżenie).

Instrukcja pusta: CONTINUE

Instrukcja CONTINUE nic nie robi. Efektem jej wykonania jest przejście do kolejnej linii programu. Najczęściej jest używana jako ostatnia instrukcja pętli.

Instrukcja wywołania procedury: CALL

Instrukcja CALL służy do wywoływania procedury. Po instrukcji CALL powinna występować nazwa procedury oraz, w nawiasach, lista jej argumentów. Liczba i rodzaj parametrów musi się oczywiście zgadzać z liczbą i rodzajem argumentów jakie zadeklarowano w definicji procedury. Wywołania funkcji nie wymagają użycia żadnych specjalnych dekoracji.

Instrukcja powrotu z podprogramu: RETURN

Wykonanie instrukcji RETURN powoduje powrót z podprogramu lub funkcji. Instrukcja ta nie ma żadnych parametrów. Wartość jaką zwraca funkcja powinna być przypisana do zmiennej o takiej samej nazwie jak nazwa funkcji.

Instrukcja zatrzymania programu: STOP

Wykonanie instrukcji STOP powoduje natychmiastowe zakończenie wykonywania programu.

Instrukcja END

Instrukcja END kończy segment. Dodatkowo jest interpretowana jako instrukcja STOP lub RETURN, w zależności od tego czy jest umieszczona na końcu segmentu głównego lub segmentu zawierającego procedurę lub funkcję.

Wejście / Wyjście

Pliki oraz urządzenia wejścia / wyjścia traktowane są jak zbiory zawierające rekordy. Pojedyncza instrukcja wprowadzająca lub wyprowadzająca dane dotyczy zawsze całego rekordu. Odpowiednikiem rekordu w pliku oraz na urządzeniu wejścia - wyjścia jest linia tekstu - ciąg znaków o stałej długości zakończany przejściem do nowej linii.. Wyprowadzania danych powoduje przejście do nowej linii - ponieważ zapisywany jest zawsze cały rekord..

Każda instrukcja wyprowadzania lub wprowadzania danych wymaga podania formatu w jakim będą wyprowadzone dane. Można określić plik jako nieformatowany, ale jego użycie jest wtedy znacznie utrudnione.

Otwarte pliki oraz urządzenia, przypisane są kanałom, z których każdy jest identyfikowany liczbą całkowitą. Domyślny kanał można wyprać pisząc w miejscu w którym wymagany jest numer - gwiazdkę. Operując na pliku – posługujemy się wyłącznie jego numerem.

Instrukcje wejścia / wyjścia

W języku FORTRAN 77, dostępne są trzy instrukcje obsługujące wejście / wyjście: READ - dla wejścia, oraz WRITE i PRINT dla wyjścia Instrukcja READ występuje w dwu wariantach:

·         READ(lista sterowania) lista-zmiennych-do-wczytania

·         READ nr-kanału, lista-zmiennych-do-wczytania

·         WRITE(lista sterowania) lista-zmiennych-do-wczytania

·         PRINT nr-kanału, lista-zmiennych-do-wczytania

Gdzie: lista zmiennych - jest ciągiem nazw zmiennych przeznaczonych do wczytania z wejścia lub wyprowadzenia na wyjście. Lista sterowania zawiera listę specyfikatorów. Pierwszy specyfikator może nie mieć nazwy - jest wtedy traktowany jako numer kanału. Drugi parametr oznacza etykietę instrukcji FORMAT zawierającej wzorzec formatowania wyprowadzonego tekstu.

Na liście zmiennych, przy wprowadzaniu lub wyprowadzaniu tablic, można użyć pewnej ciekawej konstrukcji, tzw. DO implikowanego. Przypomina ona instrukcję DO:

 

READ *, (A(i), I=1,10).

 

W tej instrukcji, do tablicy A przeczytane będą elementy od 1 do 10. Proste?

Formatowanie

Podczas większości operacji wejścia / wyjścia należy podać w jaki sposób mają być sformatowane wyprowadzane informacje. Format wybiera się podając etykietę instrukcji FORMAT. Instrukcja ta może być umieszczona w dowolnym miejscu w bloku w którym jest używana. Instrukcja FORMAT ma postać:

 

FORMAT(wzorzec),

 

gdzie wzorzec jest ciągiem stałych tekstów oraz specyfikatorów wskazujących jakiego typu zmienne będą wyprowadzana i jaką precyzją.

W tabeli przedstawiono pełną listę deskryptorów oraz znaków sterujących wyprowadzaniem informacji

 

Deskryptor

Znaczenie

rA

Pole znakowe. Przy wyprowadzaniu - długość jest równa długości pola znakowego

rAw

Pole znakowe - określona długość pola

prDw.d

pole podwójnej precyzji

prEw.d

pole rzeczywiste w postaci wykładniczej

prEw.dEe

pole rzeczywiste w postaci wykładniczej. Wartość e określa szerokość pola wykładnika

prFw.d

pole rzeczywiste w postaci stałoprzecinkowej

prGw.d

pole rzeczywiste - jak prEw.d lub prFw.d

prGw.dEe

pole rzeczywiste - jak prEw.dEe lub prFw.d

rIw

pole całkowite

rIw.m

pole całkowite. Wartość m wskazuje na ilość cyfr widocznych w polu.

iLw

pole logiczne

BN

Ignoruj spacje w polu numerycznym

BZ

Nie wiodące spacje w polu numeryczny traktuj jako 0

kP

Współczynnik skalowania

S

Sterowanie wyprowadzaniem znaku liczby. Znaczenie jak SP lub SS w zależności od procesora

SP

drukować znak plus przy wyprowadzaniu liczb dodatnich

SS

znak plus nie ma być drukowany przy wyprowadzaniu liczb dodatnich

TLs

Przesunięcie bieżącej pozycji o s znaków w lewo

TRs

Przesunięcie bieżącej pozycji o s znaków w prawo

'text'

wyprowadzenie stałego teksu

wHtext

wyprowadzenie stałego teksu

X

pomijanie znaków spacji przy wprowadzaniu

r(grupa)

Powtórzenie grupy specyfikatorów r razy

 

Poszczególne znaki oznaczają:

 

znak

Znaczenie

d

liczba znaków po przecinku

p

współczynnik skalowania

r

repetytor w postaci stała* (może być pominięty jeśli mielibyśmy napisać "1*"

w

szerokość pola w znakach

 

Formatowania można ponadto dokonać pisząc zamiast etykiety instrukcji FORMAT, dowolne wyrażenie znakowe mające postać wzorca formatowania, lub zmienna znakową zawierającą sensowny wzorzec.

Użycie plików

Aby używać plików jako wejścia, wyjścia lub przestrzeni do przechowywania danych - należy otworzyć plik i przypisać go do określonego kanału. Plik po użyciu musi być zamknięty, aby był dostępny dla innych programów. Oczywiście używać (czytać / pisać) możemy jedynie plików które są otwarte. Pliki możemy otwierać jako pliki tymczasowe - są one kasowane podczas zamykania, lub normalne - nowe - plik jest tworzony w trakcie otwierania, lub stare - plik jest jedynie otwierany.

Poza tym otwierany plik możemy określić jako plik o dostępie sekwencyjnym - a więc taki który można czytać "jak leci", oraz plik o dostępie swobodnym - w którym możemy programowo zmieniać pozycję w której będziemy pisać / czytać.

Wszystkie instrukcje dotyczące użycia plików mają podobny format. Po słowie kluczowym, w nawiasach podajemy listę specyfikatorów w postaci: (specyfikator=wartosc, ...). Pełna lista dopuszczalnych specyfikatorów znajduje się poniżej.

W języku FORTRAN77 dostępne są operacje:

·         OPEN - otwarcie pliku. Wymagane jest podanie numeru kanału któremu będzie przypisany plik oraz nazwa pliku o ile plik nie jest tymczasowy.

·         CLOSE - zamknięcie pliku. Tu podajemy numer kanału, i dodatkowo, jeśli chcemy - akcję jaką należy wykonać podczas zamykania - zachować plik na dysku czy też go skasować,

·         INQUIRE - pozwala na sprawdzenie stanu pliku, łącznie z informacją do którego kanału jest on przypisany oraz jaka jest jego nazwa

·         REWIND - przewinięcie pliku na jego początek

·         ENDFILE - obcięcie pliku w miejscu w którym plik jest czytany / pisany

·         BACKSPACE - przesunięcie znacznika odczytu / zapisu na początek poprzedniego rekordu (przesunięcie na początek następnego możemy wykonać wywołując instrukcję READ.

Specyfikatory dla instrukcji wejścia / wyjścia

Wszystkie dostępne specyfikatory zestawione są w tabelce poniżej. W poszczególnych kolumnach umieszczono: nazwę specyfikatora, znaczenie, oraz kontekst w jakim może być użyty. Poszczególne litery odpowiadają instrukcjom OPEN, CLOSE, INQUIRE i REWIND

 

Specyfikator

Znaczenie

Kontekst

UNIT

Numer urządzenia.

OCIR

IOSTAT

Nazwa zmiennej całkowitej która po zakończeniu operacji zawierać będzie kod błędu.

OCIR

ERR

Numer etykiety do której należy skoczyć jeśli podczas operacji nastąpił błąd.

OCIR

FILE

Nazwa pliku

O-I-

STATUS

Rodzaj pliku: 'NEW' - tworzy nowy plik, 'SCRATCH' - plik tymczasowy, 'OLD' - istniejący plik, 'UNKNOWN' - nie bardzo wiadomo jaki. Dla specyfikatora CLOSE - dopuszczalne są dwie wartości 'KEEP' lub 'DELETE' oznaczające odpowiednio - zachowanie pliku i usunięcie go przy zamykaniu. Domyślną wartością jest 'UNKNOWN'.

OC--

ACCESS

Metoda dostępu: 'SEQUENTIAL' - plik sekwencyjny, 'DIRECT' - plik o dostępie swobodnym. Domyślną wartością jest 'SEQUENTIAL'

O-I-

FORM

Tryb redagowania: 'FORMATTED' - plik formatowany, 'UNFORMATTED' - plik nieformatowany. Wartość domyślna zależy od typu pliku FOR gdy SEQ, UNF gdy DIR

O-I-

RECL

Długość rekordu - tylko dla plików o dostępie swobodnym.

O-I-

BLANK

Znaczenie spacji: Pola numeryczne są dopełniania znakiem spacji - 'NULL' lub zerem - 'ZERO'. Wartość domyślna: 'NULL', Dla instrukcji INQUIRE - podajemy nazwę zmiennej znakowej w której zostanie umieszczony napis 'ZERO' lub 'NULL'

O-I-

EXIST

Wskazanej zmiennej logicznej przypisuje wartość FALSE - jeśli plik związany z urządzeniem nie istnieje, lub TRUE - jeśli istnieje

--I-

OPENED

Wskazanej zmiennej logicznej przypisuje wartość TRUE - jeśli plik związany z urządzeniem jest otwarty - w przeciwnym wypadku - FALSE

--I-

NUMBER

Jeśli plik związany jest z urządzeniem (kanałem), to zmiennej całkowitej przypisuje się numer tego urządzenia. Użycie tego specyfikatora ma sens jedynie w przypadku gdy zamiast specyfikatora UNIT użyto specyfikatora FILE w instrukcji INQUIRE

--I-

NAMED

Wskazanej zmiennej logicznej przypisuje wartość TRUE - jeśli plik związany z urządzeniem ma nazwę- w przeciwnym wypadku - FALSE

--I-

NAME

Wskazanej zmiennej tekstowej przypisuje wartość będącą nazwą pliku o ile plik posiada nazwę.

--I-

SEQUENTIAL

Wskazanej zmiennej znakowej przypisuje wartość 'YES' - jeśli do urządzenia / pliku można pisać / czytać sekwencyjnie, 'NO' - jeśli nie można i 'UNKNOWN' - jeśli nie wiadomo czy można czy nie

--I-

DIRECT

Wskazanej zmiennej znakowej przypisuje wartość 'YES' - jeśli do urządzenia / pliku można uzyskać dostęp bezpośredni, 'NO' - jeśli nie można i 'UNKNOWN' - jeśli nie wiadomo czy można czy nie

--I-

FORMATTED

Wskazanej zmiennej znakowej przypisuje wartość 'YES' - jeśli do urządzenia / pliku można pisać lub czytać w sposób formatowany (o stałej długości rekordów), 'NO' - jeśli nie można i 'UNKNOWN' - jeśli nie wiadomo czy można czy nie

--I-

UNFORMATTED

Wskazanej zmiennej znakowej przypisuje wartość 'YES' - jeśli do urządzenia / pliku można pisać lub czytać w sposób nieformatowany (o zmiennej długości rekordów), 'NO' - jeśli nie można i 'UNKNOWN' - jeśli nie wiadomo czy można czy nie

--I-

NEXTREC

Numer następnego rekordu o ile plik jest otwarty z dostępem swobodnym

--I-

Definicje stałych

Stałe w programie definiuje się poleceniem PARAMERTER, podając w nawiasie listę par nazwa=wartość. Typy stałych określamy tak samo jak typy innych symboli w programie --> patrz następny paragraf.

Deklaracja zmiennych

Zmienne deklarujemy jedynie na początku bloku. Zmienne proste możemy deklarować podając nazwę typu oraz listę zmiennych które będą miały ten typ. Elementy na liście oddzielamy przecinkami Jeśli na liście występują tablice, to można od razu określić ich wymiar lub wymiary pisząc w nawiasach rozmiar tablicy. W przypadku zmiennych tekstowych definiuje się ich długość podając ją po gwiazdce (znaku mnożenia): CHARACTER*8 IDENT.

Jeśli nie zadeklarujemy typu zmiennej. określi go pierwsza litera jej nazwy. Domyślnie zmienne zaczynające się na:

·         I, J, K, L, M, N - są zmiennymi całkowitymi,

·         pozostałe - rzeczywiste.

Interpretację pierwsze litery nazwy zmiennej można zmienić używając instrukcji IMPLICIT w formacie IMPLICIT lista typów i  znaków, gdzie lista typów i znaków jest listą elementów (oddzielonych przecinkami) o formacie typ(zakres liter). Zakres liter to litery oddzielone przecinkami, lub konstrukcja od - do zapisana jako litera-litera. Przykładowo po zadeklarowaniu:

 

IMPLICIT COMPLEX(C,Z),DOUBLE PRECISION(U-X)

 

Wszystkie zmienne których nazwy rozpoczynają się od liter C lub Z będą zmiennymi zespolonymi, a zmienne o nazwach rozpoczynających się literami U, V, W, X – liczbami rzeczywistymi o podwójnej precyzji. Oczywiście tylko w takim wypadku – jeśli nie zadeklarowano ich inaczej.

Tablice muszą mieć określony rozmiar. Jeśli nie deklarujemy ich określając ich typ, musimy określić ich rozmiar za pomocą polecenia DIMENSION. Używa się go podobnie jak deklaratorów typu, z tą różnicą, że typ elementów tablicy określa pierwszy znak jej nazwy.

Podobnie jak nazwy zmiennych, w ciele funkcji definiujemy typy jej parametrów oraz typ zwracany przez funkcję (w procedurze typu parametrów). W FORTRANIE nie ma możliwości podania typów parametrów na liście parametrów. Typy definiujemy poprzez nazwę zmiennej (pierwszą literę), lub poprzez zdefiniowanie typu w taki sam sposób jak zmiennych lokalnych bloku - w jego wnętrzu.

Nadawanie wartości początkowych

Zmiennym i tablicom można nadawać wartości początkowe używając instrukcji DATA o formacie DATA lista zmiennych / lista stałych, gdzie lista zmiennych jest taką samą listą jak lista w instrukcji READ (łącznie z możliwością użycia DO implikowanego), z zastrzeżeniem, że wszystkie indeksy tablic muszą być stałymi. Lista stałych zawiera stałe odpowiednich typów. Jeśli trzeba zainicjować wiele kolejnych zmiennych tą samą wartością - możemy użyć repetytora w postaci liczba* poprzedzając nim wartość. Przykład użycia:

 

DATA X, I, (A(K), K=1,20) / 1.0, 5, 10*0.0, 4.0, 5.0, 8*1.0

 

Wartościom umieszczonym w obszarach wspólnych. inicjuje się w segmentach typu BLOCK DATA Segmenty te mają postać:

 

     BLOCK DATA nazwa_bloku_jak_w_common

            COMMON /nazwa_bloku_jak_w_common/ A, B, I(10)

            DATA A, B, I / 0.0, 5.92, 4*3, 6*0

        END

 

Wewnątrz bloku BLOCK DATA mogą występować jedynie instrukcje IMPLICIT, PARAMETER, DIMENSION, COMMON, EQUIVALENCE i DATA.

Obszary wspólne - czyli jak dzielić zmienne pomiędzy blokami

W zasadzie wszystkie dane powinny być przekazywane jako parametry funkcji i procedur (przekazywane zawsze kiedy to możliwe poprzez referencje), ale czasami prowadzi to do niepotrzebnej komplikacji kodu programu. Dzieje się tak wtedy, gdy pewne zmienne powinny być dzielone pomiędzy kilkoma modułami (np. ustawienia, pewne stałe symulacji zadawane przez użytkownik itp.). W takiej sytuacji powinniśmy zadeklarować wspólny blok danych, w którym będziemy umieszczali zmienne widoczne (dostępne) wewnątrz poszczególnych modułów.

Bloki wspólne mogą być nazwane lub nienazwane. W programie może być tylko jeden nienazwany blok wspólny, oraz dowolna liczba bloków z nazwą. Bloki wspólna są rozróżnianie poprzez ich nazwę (nienazwany - to tak jakby nazwa była pustym tekstem).

Dostęp do wspólnego bloku uzyskujemy poprzez zmienne zadeklarowane lokalnie. Komenda COMMON definicja bloku, gdzie definicja bloku zawiera nazwę bloku pomiędzy znakami '/' oraz listę zmiennych które będą umieszczone w tym bloku. Instrukcja COMMON nie definiuje nowych nazw ani ich zasięgu. Określa tylko jakie zmienne i w jakiej kolejności umieszczone są w bloku wspólnym. Poszczególne zmienne umieszczone w bloku wspólnym, mogą mieć różne nazwy w różnych blokach programu. Typy poszczególnych zmiennych (widocznych w różnych modułach) nie muszą być zgodne - chociaż niezgodność nie ma tu sensu. Nie powinno się jednak umieszczać w tym samym miejscu zmiennych liczbowych (zajmujących jedną lub dwie jednostki pamięci - tzw. jednostki numeryczne) oraz znakowych - których długość mierzona jest w jednostkach znakowych.

Przykładowo: Jeśli w jednym bloku napiszemy

 

COMMON A,B,I4,M(30)

 

a w drugim

 

COMMON X, I(32)

 

to zmienna X w drugim bloku będzie miała taką samą wartość jak zmienna A w pierwszym. Podobnie, tożsame będą zmienne i elementy tablicy B = I(1), I4=I(2), M(1)=I(3), M(2)=I(4) itd. Podobnie dzieje się w przypadku tablic wielowymiarowych, które są umieszczane w pamięci w kolejności indeksów: np. tablica M(3,3) zawiera kolejno elementy: M(1,1), M(2,1), M(3,1), M(1,2), M(2,2), ...

Jedna instrukcja COMMON może zawierać definicję wielu bloków - są one oddzielone przecinkami.

Equivalence - coś jakby unia

Jeśli zmienne lub tablice mają zajmować ten sam obszar pamięci, możemy tego zażądać stosując instrukcję EQUIVALENCE, po której podajemy listę typu (lista identyfikatorów), (...), ... .Lista identyfikatorów - jest listą nazw zmiennych które będą umieszczone w tym samym miejscu w pamięci. Jeśli wskażemy na liście komórki tablic, to również kolejne i poprzednie komórki będą na siebie nachodziły ponieważ elementy tablic zawsze zajmują ciągły obszar pamięci. W odróżnieniu od instrukcji COMMON, EQUIVALENCE definiuje równoważność lokalną - w jednym bloku programu.

Alternatywne wejście do procedury / funkcji

Do każdej funkcji lub procedury można zdefiniować dodatkowe punkty wejścia używając instrukcję ENTRY o podobnej składni jak instrukcja FUNCTION lub SYBROUTINE. Rodzaj i liczba parametrów nie musi być zgodna z listą parametrów funkcji / procedury w której definiujemy dodatkowe wejście. Nazwami wejść możemy posługiwać się tak samo jak nazwami funkcji i procedur.

Dodatkowo w procedurach można używać parametru formalnego w postaci '*' Parametr taki oznacza możliwość skoku do wybranej etykiety wewnątrz bloku procedury, jeśli wywołamy procedurę podając w jego miejscu wartość w postaci *etykieta.

Funkcje wbudowane

Funkcja

Opis

INT

konwersja liczby na liczbę całkowitą

REAL

konwersja liczby do typu rzeczywistego

DBLE

konwersja liczby do typu rzeczywistego podwójnej precyzji

CMLPX

konwersja liczby na typ zespolony.

CHAR

konwersja z liczby całkowitej na znak

CHAR

konwersja znaku do liczby całkowitej

AINT

obcięcie liczby do liczby całkowitej - wynik jest typu rzeczywistego

ANINT

Najbliższa liczba całkowita - wynik jest typu rzeczywistego

NINT

Najbliższa liczba całkowita - wynik jest typu całkowitego

ABS

wartość bezwzględna (także moduł liczby zespolonej)

MOD

reszta z dzielenia

SIGN

znak liczby

DIM

różnica dodatnia - wartość bezwzględna z różnicy argumentów)

DPROD

iloczyn dwóch liczb rzeczywistych - wynik podwójnej precyzji

MAX

maksimum - wartość maksymalna z wartości znajdujących się na liście argumentów

MIN

minimum - wartość minimalna z wartości znajdujących się na liście argumentów

LEN

zwraca długość tekstu w znakach

INDEX

zwraca pozycję podtekstu w tekście

LGE, LGT, LLE, LLT

funkcje porównujące teksty. Wynikiem jest wartość logiczna a działają podobnie jak operatory .GE., .GT., .LE., .LT.,

AIMAG

część urojona liczby zespolonej

CONJG

liczba zespolona sprzężona z liczbą podaną jako argument

SQRT

pierwiastek

EXP

eksponenta

LOG

logarytm naturalny

LOG10

logarytm dziesiętny

SIN

sinus kąta, argument wyrażony w radianach

COS

cosinus kąta, argument wyrażony w radianach

TAN

tangens kąta, argument wyrażony w radianach

ASIN

arkus sinus liczby

ACOS

arkus cosinus liczby

ATAN

arkus tangens liczby

ATAN2

arkus tangens ilorazu dwóch liczb

SINH

sinus hiperboliczny

COSH

cosinus hiperboliczny

TANH

tangens hiperboliczny

Przykład programu

Jak przykład programu proponuję program który szuka liczb pierwszych – zaprzyjaźnionych – czyli takich par liczb pierwszych które różnią się o 2. Wykorzystamy algorytm  zwany sitem Eratostenesa. Algorytm jest bardzo prosty – pracujemy na tablicy liczb, z której wykreślamy wszystkie liczby złożone Cały algorytm można zapisać jako:

1.        Tworzymy tablicę kolejnych liczb, Zawierającą N liczb (N jest największą liczbą którą będziemy sprawdzać

2.        Skreślamy w niej jedynkę

3.        Szukamy pierwszej nie wykreślonej liczby – wykreślamy wszystkie jej wielokrotności

4.        Jeśli nie sprawdzaliśmy ostatniej liczby – idziemy do kroku 3

Gotowy program zapisany jest poniżej. Tablicę liczb tworzymy tu w pliku tymczasowym, operując na nim bezpośrednio w bloku program. Jedynym działaniem które jest nam potrzebne, a którego instrukcje języka nie zawierają – jest ustawienie pozycji do zapisu / odczytu – które jest wykonywane w podprogramie.

 

C Program poszukujacy liczb pierwszych zaprzyjaznionych metoda

C sita Eratostenesa. Program jest ilustracja uzycia plikow

C tymczasowych

      PROGRAM LICZBYPIERWSZE

C N oznacza ile liczb bedziemy przeszukiwac

      PARAMETER(N=10000)

      INTEGER I,J,K,L

C otwieramy plik tymczasowy

      OPEN(UNIT=9,STATUS='SCRATCH',ACCESS='DIRECT',

     -    FORM='FORMATTED',RECL=3,ERR=900)

C i wypelniamy go od razy pomijajac liczby parzyste oprocz 2

      WRITE(9,700) 1

      WRITE(9,700) 1

      DO 100 J=1,N

        WRITE(9,700) 1

        WRITE(9,700) 0

100   CONTINUE   

      LIMIT=INT(SQRT(REAL(N)))+1

C ustawiamy poczatkowy numer liczby pierwszej

      I=3

C szukamy kolejnej liczby pierwszej

101   CALL POSITION(I)

102   IF (I.GT.LIMIT) GOTO 120

      READ(9,700) L

      IF (L.EQ.0) THEN

        I = I+1

        GOTO 102

      ELSE

C wpisujemy 0 na wszystkie pozycje bedace wielokrotnosciami znalezionej

        DO 110 K=2*I,N,I

          CALL POSITION(K)

          WRITE(9,700) 0

110     CONTINUE

        I = I+1   

        GOTO 101

      END IF

C wypisujemy liczby zaprzyjaznione

120   CALL POSITION(1)

      I=1

      DO 130 J=1,N

        READ(9,700) L

        IF (L.EQ.1) THEN

          IF (J-I.EQ.2) THEN

            WRITE(*,703) I,J

          END IF

          I=J

        END IF

130   CONTINUE

      CLOSE(UNIT=9)

      STOP

C formaty uzywane w programie

700   FORMAT(I1)

701   FORMAT(I12)

702   FORMAT('Blad programu')

703   FORMAT(I10,I10)

C wypisanie komunikatu o bledzie

900   WRITE(*,702)

      STOP

      END

     

C PODPROGRAM USTAWIAJACY WSKAZNIK PLIKU NA WYBRANEJ POZYCJI

      SUBROUTINE POSITION(I)

      INTEGER I,J

      INQUIRE(UNIT=9,NEXTREC=J)

      IF (J-I) 600,610,620

C PRZESUN SIE DO PRZODU     

600   DO 601 K=J,I-1

        READ(9,700) L

601   CONTINUE

C POZYCJA JEST DOBRA

610   RETURN

C PRZESUN SIE DO TYLU

620   REWIND(9)

      DO 622 K=1,I-1

        READ(9,700) L

622   CONTINUE

700   FORMAT(I1)

      END

Zadania

1.        Proszę zainstalować kompilator języka FORTRAN – g77 oraz przygotować sobie 3 pliki wsadowe służące do edycji, kompilacji i uruchamiania bieżącego programu.

2.        Proszę skompilować i uruchomić program „99 bottles of beer” w języku FORTRAN IV.

3.        Przerabiając program z poprzedniego zadania, proszę napisać najprostszy program w języku fortran – typu ‘hello world’, skompilować go i uruchomić.

4.        Proszę napisać program który policzy miejsca zerowe równania kwadratowego. Na wejściu programu określamy współczynniki A, B, C równania Ax2+Bx+C=0

5.        Proszę napisać program znajdujący miejsce zerowe wielomianu czwartego stopnia w przedziale 0..1, metodą bisekcji.

6.        Proszę napisać program wyliczający kolejne liczby pierwsze metodą sita Eratostenesa. Co zrobić jeśli tablica nie mieści się w pamięci?

a.        pracujemy na pliku tymczasowym – jedynej dostępnej strukturze dynamicznej

b.       pracujemy na fragmencie tablicy – zapisując znalezione liczby do pliku wyjściowego

Proszę przemyśleć metody optymalizacji.

 

 



[1] Wcięcia w programach pisanych w języku FORTRAN nie są zwyczajowe – ale bardzo ułatwiają zrozumienie kodu – dlatego w przykładach będziemy ich używać. W przykładach dostępnych w literaturze – wcięć zazwyczaj się nie stosuje.