Pole

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ů.

Deklarace pole

Pole se deklaruje uvedením typu prvků pole a dimenzí.

Statická pole

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)

Dynamická pole

Č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.

Automatická 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.

Opičárny s polama

Dimensování

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)

Initializace

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 /)

Podpole

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

Základní operace s poli

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)

Maticové if

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

Předávání polí

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.

Klasický styl

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

Moduly

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

Příklad

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