Fortran
Nowoczesne alternatywy dla cech historycznych
Szukaj…
Domniemane typy zmiennych
Kiedy Fortran został pierwotnie opracowany, pamięć była na wagę złota. Zmienne i nazwy procedur mogą mieć maksymalnie 6 znaków, a zmienne często są niejawnie wpisywane . Oznacza to, że pierwsza litera nazwy zmiennej określa jej typ.
- zmienne rozpoczynające się od i, j, ..., n są
integer
- wszystko inne (a, b, ..., h oraz o, p, ..., z) są
real
Programy takie jak następujące są dopuszczalne Fortran:
program badbadnotgood
j = 4
key = 5 ! only the first letter determines the type
x = 3.142
print*, "j = ", j, "key = ", key, "x = ", x
end program badbadnotgood
Możesz nawet zdefiniować swoje własne reguły niejawne za pomocą instrukcji implicit
:
! all variables are real by default
implicit real (a-z)
lub
! variables starting with x, y, z are complex
! variables starting with c, s are character with length of 4 bytes
! and all other letters have their default implicit type
implicit complex (x,y,z), character*4 (c,s)
Wpisywanie niejawne nie jest już uważane za najlepszą praktykę. Bardzo łatwo jest popełnić błąd przy użyciu pisania niejawnego, ponieważ literówki mogą pozostać niezauważone, np
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Ten program z radością uruchomi się i zrobi coś złego.
Aby wyłączyć niejawne pisanie, można użyć implicit none
instrukcji implicit none
.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Gdybyśmy używali implicit none
w programie oops
powyżej, kompilator będzie zauważyli natychmiast, a produkowane błąd.
Instrukcja arytmetyczna if
Arytmetyczna instrukcja if
pozwala na użycie trzech gałęzi w zależności od wyniku wyrażenia arytmetycznego
if (arith_expr) label1, label2, label3
To, if
instrukcja przenosi przepływ sterowania do jednej z etykiet w kodzie. Jeśli wynik arith_expr
jest ujemny, label1
to label1
, jeśli wynikiem jest zero, label2
jest label2
, a jeśli wynik jest dodatni, stosowana jest ostatnia label3
. Arytmetyka, if
wymaga wszystkich trzech etykiet, ale pozwala na ponowne użycie etykiet, dlatego to stwierdzenie można uprościć do dwóch gałęzi, if
.
Przykłady:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Teraz ta funkcja jest przestarzała, a taką samą funkcjonalność oferuje instrukcja if
i konstrukcja if-else
. Na przykład fragment
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
można zapisać jako konstrukcję if-else
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Instrukcja if
zastępująca
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
może
if (X<=0) print*, "Negative or zero"
Nieblokowane konstrukcje DO
Non-block do
wygląda jak konstruować
integer i
do 100, i=1, 5
100 print *, i
Oznacza to, że oznaczone etykietą wypowiedzenie nie jest instrukcją continue
. Istnieją różne ograniczenia dotyczące oświadczenia, które mogą być użyte jako oświadczenie o zakończeniu, a całość jest ogólnie bardzo myląca.
Taka konstrukcja nieblokowa może zostać przepisana w formie bloku jako
integer i
do 100 i=1,5
print *, i
100 continue
lub lepiej, używając oświadczenia o end do
umowy,
integer i
do i=1,5
print *, i
end do
Alternatywny zwrot
Alternatywny zwrot to narzędzie do kontrolowania przepływu wykonania po powrocie z podprogramu. Jest często używany jako forma obsługi błędów:
real x
call sub(x, 1, *100, *200)
print*, "Success:", x
stop
100 print*, "Negative input value"
stop
200 print*, "Input value too large"
stop
end
subroutine sub(x, i, *, *)
real, intent(out) :: x
integer, intent(in) :: i
if (i<0) return 1
if (i>10) return 2
x = i
end subroutine
Alternatywny zwrot jest oznaczony argumentami *
na podrzędnej liście argumentów podprogramu.
W powyższym zestawieniu call
*100
i *200
odnoszą się do wyciągów oznaczonych odpowiednio 100
i 200
.
W samym podprogramie instrukcje return
odpowiadające alternatywnemu zwrotowi mają liczbę. Liczba ta nie jest wartością zwracaną, ale oznacza dostarczoną etykietę, której wykonanie jest przekazywane po powrocie. W takim przypadku return 1
przekazuje wykonanie do instrukcji oznaczonej jako 100
a return 2
przekazuje wykonanie do instrukcji oznaczonej jako 200
. Nieadekwatna instrukcja return
lub zakończenie wykonywania podprogramu bez instrukcji return
przekazuje wykonanie bezpośrednio po instrukcji call.
Alternatywna składnia zwrotna bardzo różni się od innych form asocjacji argumentów, a funkcja wprowadza kontrolę przepływu w przeciwieństwie do współczesnych gustów. Bardziej przyjemną kontrolą przepływu można zarządzać przez zwrócenie liczb całkowitych „statusu” kodu.
real x
integer status
call sub(x, 1, status)
select case (status)
case (0)
print*, "Success:", x
case (1)
print*, "Negative input value"
case (2)
print*, "Input value too large"
end select
end
subroutine sub(x, i, status)
real, intent(out) :: x
integer, intent(in) :: i
integer, intent(out) :: status
status = 0
if (i<0) then
status = 1
else if (i>10)
status = 2
else
x = i
end if
end subroutine
Naprawiono formularz źródłowy
Fortran pierwotnie został zaprojektowany dla formatu o stałym formacie opartym na 80-kolumnowej karcie perforowanej:
Tak: To jest linia własnego kodu autora
Zostały one utworzone na maszynie do dziurkowania kart, podobnie jak to:
Obrazy są oryginalną fotografią autora
Format, jak pokazano na zilustrowanej przykładowej karcie, miał pierwsze pięć kolumn zarezerwowanych dla etykiet wyciągów. Pierwsza kolumna posłużyła do oznaczenia komentarzy literą C. Szósta kolumna została użyta do oznaczenia kontynuacji instrukcji (poprzez wstawienie dowolnego znaku innego niż zero „0”). Ostatnie 8 kolumn wykorzystano do identyfikacji i sekwencjonowania kart, co było bardzo cenne, jeśli upuściłeś swoją talię kart na podłogę! Kodowanie znaków dla kart perforowanych miało ograniczony zestaw znaków i było pisane tylko dużymi literami. W rezultacie programy Fortran wyglądały tak:
DIMENSION A(10) 00000001
C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002
WRITE (6,100) 00000003
100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004
1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005
2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006
STOP 00000007
END 00000008
Znak spacji był również wszędzie ignorowany, z wyjątkiem wewnątrz stałej postaci Hollerith (jak pokazano powyżej). Oznaczało to, że spacje mogą występować w zarezerwowanych słowach i stałych lub całkowicie pominięte. Miało to efekt uboczny niektórych dość mylących stwierdzeń, takich jak:
DO 1 I = 1.0
jest przypisaniem do zmiennej DO1I
podczas gdy:
DO1I = 1,0
jest w rzeczywistości pętlą DO
zmiennej I
Modern Fortran nie wymaga teraz tej stałej formy wprowadzania danych i zezwala na dowolną formę przy użyciu dowolnych kolumn. Komentarze są teraz oznaczone symbolem !
które można również dołączyć do wiersza instrukcji. Spacje nie są teraz nigdzie dozwolone i muszą być używane jako separatory, podobnie jak w większości innych języków. Powyższy program można napisać we współczesnym Fortranie jako:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
Chociaż kontynuacja w starym stylu nie jest już używana, powyższy przykład pokazuje, że nadal będą występować bardzo długie instrukcje. Modern Fortran używa symbolu &
na końcu i na początku kontynuacji. Na przykład moglibyśmy napisać powyższe w bardziej czytelnej formie:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still &
&GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT &
&CONTINUATION notation"
Wspólne bloki
We wczesnych formach Fortrana jedynym mechanizmem tworzenia globalnego magazynu zmiennych widocznego z podprogramów i funkcji jest użycie mechanizmu COMMON
bloku. Dzięki temu sekwencje zmiennych mogły być wspólne i wspólne.
Oprócz nazwanych wspólnych bloków może również istnieć pusty (nienazwany) wspólny blok.
Pusty wspólny blok może być zadeklarowany jak
common i, j
podczas gdy nazwane variables
blokowe można zadeklarować jak
common /variables/ i, j
Jako kompletny przykład możemy sobie wyobrazić magazyn sterty, który jest używany przez procedury, które mogą dodawać i usuwać wartości:
PROGRAM STACKING
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = 0
READ *, IVAL
CALL PUSH(IVAL)
CALL POP(IVAL)
END
SUBROUTINE PUSH(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = ICOUNT + 1
ISTACK(ICOUNT) = IVAL
RETURN
END
SUBROUTINE POP(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
IVAL = ISTACK(ICOUNT)
ICOUNT = ICOUNT - 1
RETURN
END
Za pomocą wspólnych instrukcji można niejawnie zadeklarować typ zmiennej i określić atrybut dimension
. Samo to zachowanie jest często wystarczającym źródłem zamieszania. Co więcej, implikowane powiązanie pamięci i wymagania dotyczące powtarzających się definicji w jednostkach programu sprawiają, że stosowanie typowych bloków jest podatne na błędy.
Wreszcie, wspólne bloki są bardzo ograniczone w obiektach, które zawierają. Na przykład tablica we wspólnym bloku musi mieć jawny rozmiar; przydzielane obiekty mogą nie wystąpić; typy pochodne nie mogą mieć domyślnej inicjalizacji.
We współczesnym Fortranie to dzielenie się zmiennymi można obsłużyć za pomocą modułów . Powyższy przykład można zapisać jako:
module heap
implicit none
! In Fortran 2008 all module variables are implicitly saved
integer, save :: count = 0
integer, save :: stack(1023)
end module heap
program stacking
implicit none
integer val
read *, val
call push(val)
call pop(val)
contains
subroutine push(val)
use heap, only : count, stack
integer val
count = count + 1
stack(count) = val
end subroutine push
subroutine pop(val)
use heap, only : count, stack
integer val
val = stack(count)
count = count - 1
end subroutine pop
end program stacking
Nazwane i puste wspólne bloki mają nieco inne zachowania. Nutowy:
- obiekty w nazwanych wspólnych blokach można wstępnie zdefiniować; przedmioty w pustym wspólnym obiekcie nie będą
- obiekty w pustych wspólnych blokach zachowują się tak, jakby wspólny blok miał atrybut
save
; obiekty w nazwanych wspólnych blokach bez atrybutusave
mogą stać się niezdefiniowane, gdy blok nie wchodzi w zakres aktywnej jednostki programowej
Ten ostatni punkt można przeciwstawić zachowaniu zmiennych modułowych we współczesnym kodzie. Wszystkie zmienne modułu w Fortran 2008 są domyślnie zapisywane i nie stają się niezdefiniowane, gdy moduł wykracza poza zakres. Przed Fortran 2008 zmienne modułowe, takie jak zmienne w nazwanych wspólnych blokach, również stałyby się niezdefiniowane, gdy moduł wykraczał poza zakres.
Przypisano GOTO
Przypisany GOTO używa zmiennej całkowitej, do której przypisana jest etykieta instrukcji za pomocą instrukcji ASSIGN.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Przypisane GOTO jest przestarzałe w Fortran 90 i usunięte w Fortran 95 i nowszych. W nowoczesnym kodzie można tego uniknąć, stosując procedury, procedury wewnętrzne, wskaźniki procedur i inne funkcje.
Obliczone GOTO
Obliczone GOTO pozwala na rozgałęzienie programu zgodnie z wartością wyrażenia całkowitego.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Jeśli scalar-integer-expression
jest równe 1, program kontynuuje pracę na etykiecie instrukcji label_1
, jeśli jest równy 2, przechodzi do label_2
i tak dalej. Jeśli jest mniejsza niż 1
lub większa niż n
program kontynuuje w następnym wierszu.
Przykład:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
przejdzie do etykiety oświadczenia 20.
Ta forma goto
jest przestarzała w Fortran 95 i późniejszych wersjach, zastępując ją konstrukcją select case
.
Przypisane specyfikatory formatu
Przed Fortranem 95 możliwe było stosowanie przypisanych formatów dla danych wejściowych lub wyjściowych. Rozważać
integer i, fmt
read *, i
assign 100 to fmt
if (i<100000) assign 200 to fmt
print fmt, i
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
Instrukcja assign
przypisuje etykietę instrukcji do zmiennej całkowitej. Ta zmienna całkowita jest później używana jako specyfikator formatu w instrukcji print
.
Takie przypisanie specyfikatora formatu zostało usunięte w Fortran 95. Zamiast tego, bardziej nowoczesny kod może używać innej formy kontroli przepływu wykonywania
integer i
read *, i
if (i<100000) then
print 100, i
else
print 200, i
end if
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
lub zmienna znakowa może być użyta jako specyfikator formatu
character(29), target :: big_fmt='("This is a big number", I10)'
character(30), target :: small_fmt='("This is a small number", I6)'
character(:), pointer :: fmt
integer i
read *, i
fmt=>big_fmt
if (i<100000) fmt=>small_fmt
print fmt, i
end
Funkcje instrukcji
Rozważ program
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Tutaj f
jest funkcją instrukcji. Ma typ wyniku liczb całkowitych, biorąc jeden argument fikcyjny liczby całkowitej. 1
Taka funkcja instrukcji istnieje w zakresie, w którym jest zdefiniowana. W szczególności ma dostęp do zmiennych i nazwanych stałych dostępnych w tym zakresie.
Jednak funkcje instrukcji podlegają wielu ograniczeniom i mogą być mylące (patrząc na przypadkowy wygląd jak instrukcja przypisania elementu tablicy). Ważne ograniczenia to:
- wynik funkcji i fikcyjne argumenty muszą być skalarne
- fikcyjne argumenty mają ten sam zakres co funkcja
- funkcje instrukcji nie mają lokalnych zmiennych
- funkcje instrukcji nie mogą być przekazywane jako rzeczywiste argumenty
Główne zalety funkcji instrukcji są powtarzane przez funkcje wewnętrzne
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Funkcje wewnętrzne nie podlegają wymienionym wyżej ograniczeniom, chociaż być może warto zauważyć, że wewnętrzny podprogram może nie zawierać dalszego wewnętrznego podprogramu (ale może zawierać funkcję instrukcji).
Funkcje wewnętrzne mają swój zakres, ale także dostępne powiązanie hosta.
1 W prawdziwych starych przykładach kodu nie byłoby niczym niezwykłym, że fikcyjne argumenty funkcji instrukcji są domyślnie wpisywane, nawet jeśli wynik ma typ jawny.