Kurs FORTRANU | ||
---|---|---|
Předcházející | Další |
Pole jsou nejzákladnější datová struktura. Protože jsou nesmírně často používanou praktikou při programování matematických algoritmů, ma pro ně Fortran velmi silnou jazykovou podporu. Použití polí je často nepřirozenější způsob zápisu a v případě, že máme patřičnou hardware podporu, vede i k podstatnému zrychlení programů.
Pole se deklaruje uvedením typu prvků pole a dimenzí.
Definice statických polí je prostá uvede se dimenze pole prostřednictvím celočíslené konstanty nebo výrazu, který může být vyhodnocen již při kompilaci.
real :: pole(100)
tento příkaz deklaruje reálné pole o 100 prvcích. První index má hodnotu 1 a poslední 100. K jednotlivým prvkům přistupujeme pomocí indexu zapsaného za jméno pole do závorek. Například v následujícím fragmentu programu přiřadíme do prvku 24 hodnotu -1.0 a pak ji vytiskneme.
pole(24) = -1.0 write(*,*) pole(24)
Jako příklad složitejší deklarace pole ovšem s naprosto stejným výsledkem uvedeme:
integer, parameter :: ndim = 50 real :: pole(2*ndim)
Často ovšem neznáme velikost pole v okamžiku kompilace programu. To se často řeší nadimenzováním pole na mnohem větší velikost. Zřejme to ovšem nelze považovat za optimální řešení. Mnohem lépe je deklarovat pole v průběhu běhu programu. V tomto případě uvedene při deklaraci pole do jako mez dvojtečku.
Na vytvoření pole v průbehu programu pak slouži funkce allocate s argumentem pole a jeho dimenze. V příkladu je opět uvedno vytváření 100 prvkového pole.
real :: pole(:) allocate(pole(100))
Práce i chování takto vytvořeného pole ja naprosto stejná jako u pole statického. Navíc jej lze příkazem deallocate kdykoli zrušit. Neexistuje ovšem, vzhledem k náročnosti implementace, funkce na změnu velikosti pole.
Často je třeba v podprogramu vytvořit pole a po jeho skončení toto pole opět zrušit. Ručně to lze provést prostřednictvím dynamických polí, ale ve Fortranu exsituje ještě jednodužší možnost. V případě, že v podprogramu (!) dáme jako dimenzi některý z celočíselných parametrů podprogramu, uděla za nás postřebné alokace a dealokace překladač. Je to stejně náročná metoda na systémové prostředky jako udělat to ručně, ovšem značně to zjednodužší text programu a umožní oddělit vlastní algoritmus od počítačově závyslých prostředků.
subroutine demo(ndim) real :: pole(ndim) pole(ndim) = -1.0 write(*,*) pole(ndim) end subroutine
V tomto příkladě jsme jako parametr procedury předali dimenzi pole a s ním pak udělali obvyklé procedury. Číslo ndim musí být větší než nula.
Dimensi můžeme také popsat v atributu dimension:
real, dimension(3) :: pole
nebo můžeme uvést indexování jinak než od 1:
real :: pole1 (0:99) ! C-like real :: pole2 (-10:10)
případně můžeme použít více dimenzí
real :: pole0 (100,100) real :: pole1 (0:99,0:99) ! C-like real :: pole2 (-10:10,-10:10)
Krátké pole lze initialozovat prvkek po prvku prostřednictvím tzv. konstruktoru. V tomto případě máme pole s třemi prvky a inicialuzujeme jejich jednotlivé hodnoty.
pole = (/ 0.0, 1.0, 2.0 /)
Z polí můžeme dělat výřezy. Například rozsekat matici na vektory.
real :: a(3,3), b(3), c(2,2) a = 1.0 c = a(1:2,1:2) b = a(1,:) ! totozne s b = a(1,1:3) ! nebo dokonce b = a(2,3:1:-1) ! priradi zinvertovany vektor
Všechny základní operace které můžeme provádět s čísli můžeme provádět i s poli. V tomto případě se provádějí operace postupně nad jednotlivými prvky. V příkladu je uvedeno maticové sčítání. Složkový a maticový součin.
real :: a(3,3), b(3,3), c(3,3) a = 1.0 b = -1.0 c = a + b c = a*b c = matmul(a,b)
S poli tedy jdou dělat všechny elementární operace jako s čísly a navíc můžeme použít řadu funkcí:
real :: a(3,3), b(3,3), c(3), d(3) a = 1.0 b = 0.0 c = (/ 1.0, 0.0, 0.0 /) d = (/ 0.0, 1.0, 0.0 /) ! soucet prvku write(*,*) sum(c*d) ! totozné s ! s = 0.0 ! do i = 1,3 ! s = s + c(i)*d(i) ! enddo ! write(*,*) s ! nejvetsi hodnota write(*,*) maxval(c) ! vsechny hodnoty mensi nez 1 write(*,*) all(c < 1.0) ! dimense pole write(*,*) count(c) ! skalarni soucet write(*,*) dot_produkt(c,d) ! vektorovy soucin write(*,*) matmul(a,b) ! trasnponovani write(*,*) transpose(a), transpose(c)
je příkaz where.
real :: a(100), b(100) a = 0.0 a(1:10) = 1.0 where( a > 0.0 ) b = 1.0 elsewhere b = -1.0 endwhere ! toto je ekvivalentni s do i = 1,100 if( i ≤ 10 )then a(i) = 1.0 else a(i) = 0.0 endif enddo do i = 1, 100 if( a(i) > 0.0 )then b(i) = 1.0 else b(i) = -1.0 endif enddo
V principu lze předávat pole dvojím způsobem tak aby volané procedura věděla o jeho dimenzích. Pokud dimenze neuvedeme pak musí (!) mít procedura informace o polích buď uvedením interface nebo uvedením v modulu (pak dělá interface samotný kompiler). Pokud nejsme proti uvádění dimensí nemusí být procedura ani v modulu anu nemusí mít uvedený interface. Pokud tyhle zasady nedodržujeme překlad proběhne v pořádku, ale problémy se projeví při běhu programu.
V tomto případě předává nadřazená procedura dimense poli ručně. Ve volané proceduře musí být dimenzované předávané pole s těmihle dimensemi.
Program vrsek integer, paramerter :: ndim = 10 real :: a(ndim), b(ndim, ndim) call spodek(ndim,a,ndim,ndim,b) end program subroutine spodek(adim, a, bdim1, bdim2, b) integer :: adim, bdim1, bdim2 real :: a(adim), b(bdim1,bdim2) a = 0 b = 0 end subroutine
V tomto případě předává nadřazená procedura dimense poli automaticky. Ve nemusi být dimense uvedeny. Případně můžeme dimese zistit pomocí standarních funkcí.
Program vrsek use spodky integer, paramerter :: ndim = 10 real :: a(ndim), b(ndim, ndim) call spodek(a,b) end program Module spodky contains subroutine spodek(a, b) integer :: adim, bdim1, bdim2 real :: a(:), b(:,:) adim = size(a) bdim1 = size(b,1) bdim2 = size(b,2) a = 0 b = 0 end subroutine end module
Operace s maticemi jsou základní operace prováděné s poli. Asi jedna z nejzákladnějších věcí, kterou lze s maticemi provádět je řešit soustavy rovnic použitím Gaussovi eliminační metody. Předvedeme nejjednodužší verzi, bez pivotování.
Vzorce i algoritmus jsou všeobecně známy. My použijeme upravený algoritmus s Hřebíčka. Doporucuju srovnat s puvodnim algoritmem bez pouziti maticoveho zapisu. Je vhodné připomenout, že celý postup by šlo zapsat pomocí maticových operací, ovšem vzhledem k úspoře místa a prostředků to není vhodné.
subroutine Gauel(n,a,b) ! n je dimense matice, b je vektor pravych stran, na vystupu ! obsahuje reseni integer :: n real :: a(n,n),b(n) do k = 1, n ! vydeleni prvku vpravo od diagonaly diagonalnim prvkem a(k,k+1:n) = a(k,k+1:n)/a(k,k) b(k) = b(k)/a(k,k) ! eliminace prvku pod diagonalou do i = k + 1, n do j = k + 1, n a(i,j) = a(i,j) - a(i,k)*a(k,j) b(i) = b(i) - a(i,k)*b(k) enddo enddo enddo ! zpetna substituce do i = n - 1, 1, -1 do k = i + 1, n b(i) = b(i) - a(i,k)*b(k) enddo enddo end subroutine Program tgauel real :: a(3,3),b(3) a = 1 b = 0 call gauel(3,a,b) write(*,*) b end
Předcházející | Domů | Další |
Vstup/Vystup | LU Rozklad |