Fortran
Procedury - funkcje i podprogramy
Szukaj…
Uwagi
Funkcje i podprogramy w połączeniu z modułami są narzędziami do podziału programu na jednostki. Dzięki temu program jest bardziej czytelny i zarządzalny. Każda z tych jednostek może być uważana za część kodu, który idealnie można skompilować i przetestować osobno. Program główny może wywoływać (lub wywoływać) takie podprogramy (funkcje lub podprogramy) w celu wykonania zadania.
Funkcje i podprogramy różnią się w następującym znaczeniu:
- Funkcje zwracają pojedynczy obiekt i - zwykle - nie zmieniają wartości jego argumentów (tj. Działają one jak funkcja matematyczna!);
- Podprogramy zwykle wykonują bardziej skomplikowane zadanie i zwykle zmieniają swoje argumenty (jeśli takie istnieją), a także inne zmienne (np. Zadeklarowane w module zawierającym podprogram).
Funkcje i podprogramy łącznie są nazywane procedurami . (W dalszej części użyjemy czasownika „call” jako synonimu „invoke”, nawet jeśli technicznie procedury do call
są subroutine
, podczas gdy function
pojawiają się jako prawa strona przypisania lub wyrażeń.)
Składnia funkcji
Funkcje można pisać przy użyciu kilku rodzajów składni
function name()
integer name
name = 42
end function
integer function name()
name = 42
end function
function name() result(res)
integer res
res = 42
end function
Funkcje zwracają wartości poprzez wynik funkcji . O ile instrukcja funkcji nie zawiera klauzuli wynikowej, result
funkcji ma taką samą nazwę jak funkcja. Z result
funkcji jest wynikiem podanym przez result
. W każdym z dwóch pierwszych przykładów powyżej wynik funkcji podany jest po name
; w trzecim przez res
.
Wynik funkcji musi zostać zdefiniowany podczas wykonywania funkcji.
Funkcje pozwalają używać specjalnych prefiksów.
Czysta funkcja oznacza, że ta funkcja nie ma skutków ubocznych:
pure real function square(x)
real, intent(in) :: x
square = x * x
end function
Funkcja elementarna jest zdefiniowana jako operator skalarny, ale można ją wywołać za pomocą tablicy jako argumentu rzeczywistego, w którym to przypadku funkcja zostanie zastosowana elementarnie. O ile nie podano impure
prefiksu (wprowadzonego w Fortran 2008), funkcja elementarna jest również funkcją czystą .
elemental real function square(x)
real, intent(in) :: x
square = x * x
end function
Oświadczenie zwrotne
Za pomocą instrukcji return
można wyjść z funkcji i podprogramu. W przeciwieństwie do wielu innych języków programowania nie jest on używany do ustawiania wartości zwracanej.
real function f(x)
real, intent(in) :: x
integer :: i
f = x
do i = 1, 10
f = sqrt(f) - 1.0
if (f < 0) then
f = -1000.
return
end if
end do
end function
Ta funkcja wykonuje obliczenia iteracyjne. Jeśli wartość f
staje się ujemna, funkcja zwraca wartość -1000.
Procedury rekurencyjne
W Fortranie funkcje i podprogramy muszą być jawnie zadeklarowane jako rekurencyjne , jeśli mają się ponownie wywoływać, bezpośrednio lub pośrednio. Zatem rekurencyjna implementacja serii Fibonacciego może wyglądać następująco:
recursive function fibonacci(term) result(fibo)
integer, intent(in) :: term
integer :: fibo
if (term <= 1) then
fibo = 1
else
fibo = fibonacci(term-1) + fibonacci(term-2)
end if
end function fibonacci
Dozwolony jest inny przykład do obliczenia silni:
recursive function factorial(n) result(f)
integer :: f
integer, intent(in) :: n
if(n == 0) then
f = 1
else
f = n * f(n-1)
end if
end function factorial
Aby funkcja odwoływała się bezpośrednio do siebie rekurencyjnie, jej definicja musi używać sufiksu result
. Funkcja nie może być zarówno recursive
jak i elemental
.
The Intent of Dummy Arguments
Atrybut intent
fałszywego argumentu w podprogramie lub funkcji deklaruje jego zamierzone użycie. Składnia jest albo jedna z
intent(IN)
intent(OUT)
intent(INOUT)
Rozważmy na przykład tę funkcję:
real function f(x)
real, intent(IN) :: x
f = x*x
end function
intent(IN)
określa, że (pozointerowy) fikcyjny argument x
nigdy nie może zostać zdefiniowany lub stać się niezdefiniowany podczas funkcji lub jej inicjalizacji. Jeśli fikcyjny wskaźnik wskaźnika ma intent(IN)
atrybutu intent(IN)
, dotyczy to jego powiązania.
intent(OUT)
dla fikcyjnego argumentu niepointerowego oznacza, że fikcyjny argument staje się niezdefiniowany po wywołaniu podprogramu (z wyjątkiem jakichkolwiek komponentów typu pochodnego z domyślną inicjalizacją) i należy go ustawić podczas wykonywania. Rzeczywisty argument przekazany jako fikcyjny argument musi być możliwy do zdefiniowania: przekazywanie stałej nazwanej lub dosłownej lub wyrażenia jest niedozwolone.
Podobnie jak poprzednio, jeśli intent(OUT)
jest argument pozorny wskaźnika intent(OUT)
status powiązania wskaźnika staje się niezdefiniowany. Rzeczywistym argumentem tutaj musi być zmienna wskaźnikowa.
intent(INOUT)
określa, że rzeczywisty argument jest definiowalny i nadaje się zarówno do przekazywania, jak i zwracania danych z procedury.
Wreszcie, fikcyjny argument może być pozbawiony atrybutu intent
. Taki fikcyjny argument ma zastosowanie ograniczone przez przekazany argument.
Rozważmy na przykład
integer :: i = 0
call sub(i, .TRUE.)
call sub(1, .FALSE.)
end
subroutine sub(i, update)
integer i
logical, intent(in) :: update
if (update) i = i+1
end subroutine
Argument i
może mieć atrybutu intent
który zezwala na oba wywołania podprogramu programu głównego.
Odwołanie do procedury
Aby funkcja lub podprogram był użyteczny, należy się do niego odwoływać. Do podprogramu odwołuje się instrukcja call
call sub(...)
i funkcja w wyrażeniu. W przeciwieństwie do wielu innych języków, wyrażenie nie tworzy pełnej instrukcji, więc odwołanie do funkcji jest często widziane w instrukcji przypisania lub używane w inny sposób:
x = func(...)
y = 1 + 2*func(...)
Istnieją trzy sposoby wyznaczenia odwoływanej procedury:
- jako nazwa procedury lub wskaźnik procedury
- komponent procedury obiektu typu pochodnego
- nazwa powiązania procedury powiązanej z typem
Pierwszy można uznać za
procedure(), pointer :: sub_ptr=>sub
call sub() ! With no argument list the parentheses are optional
call sub_ptr()
end
subroutine sub()
end subroutine
a dwa ostatnie jako
module mod
type t
procedure(sub), pointer, nopass :: sub_ptr=>sub
contains
procedure, nopass :: sub
end type
contains
subroutine sub()
end subroutine
end module
use mod
type(t) x
call x%sub_ptr() ! Procedure component
call x%sub() ! Binding name
end
W przypadku procedury z fałszywymi argumentami odwołanie wymaga odpowiednich rzeczywistych argumentów, chociaż opcjonalne argumenty fałszywe mogą nie zostać podane.
Rozważ podprogram
subroutine sub(a, b, c)
integer a, b
integer, optional :: c
end subroutine
Można się do tego odwołać na dwa następujące sposoby
call sub(1, 2, 3) ! Passing to the optional dummy c
call sub(1, 2) ! Not passing to the optional dummy c
Jest to tak zwane odwołanie pozycyjne : rzeczywiste argumenty są powiązane na podstawie pozycji na listach argumentów. W tym przypadku manekin a
jest powiązany z 1
, b
z 2
a c
(jeśli określono) z 3
.
Alternatywnie można użyć odwołania do słów kluczowych , gdy procedura ma dostępny jawny interfejs
call sub(a=1, b=2, c=3)
call sub(a=1, b=2)
który jest taki sam jak powyżej.
Jednak w przypadku słów kluczowych rzeczywiste argumenty mogą być oferowane w dowolnej kolejności
call sub(b=2, c=3, a=1)
call sub(b=2, a=1)
Można stosować zarówno odwołania do pozycji, jak i do słów kluczowych
call sub(1, c=3, b=2)
o ile słowo kluczowe jest podane dla każdego argumentu następującego po pierwszym wystąpieniu słowa kluczowego
call sub(b=2, 1, 3) ! Not valid: all keywords must be specified
Wartość odwoływania się do słów kluczowych jest szczególnie wyraźna, gdy istnieje wiele opcjonalnych fałszywych argumentów, jak pokazano poniżej, jeśli w definicji podprogramu powyżej b
były również opcjonalne
call sub(1, c=3) ! Optional b is not passed
Listy argumentów dla procedur związanych z typem lub wskaźników procedur składowych z przekazanym argumentem są rozpatrywane osobno.