Uvod v objektno usmerjen konceptov z uporabo Fortran90
Nextranks

Uvod v objektno usmerjen konceptov z uporabo Fortran90

Viktor K. Decyk Oddelek za fiziko, University of California v Los Angelesu Los Angeles, CA 90095-1547 in Jet Propulsion Laboratory, California Institute of Technology Pasadena, California 91109 e-pošta: decyk@physics.ucla.edu Charles D. Norton Jet Propulsion Laboratory, California Institute of Technology MS 168-522, 4800 Oak Grove Drive Pasadena, California 91109 e-pošta: Charles.D.Norton@jpl.nasa.gov Boleslaw K. Szymański Oddelek za računalništvo Računanje in znanstveni raziskovalni center (SCOREC) 110 Osmič Street, Rensselaer Polytechnic Institute, Troy, NY 12180-3590 e-pošta: szymansk@cs.rpi.edu Povzetek Fortran90 je sodoben, zmogljiv jezik s funkcijami, ki podpirajo pomembne nove programske koncepte, vključno s tistimi za objektno programiranje. Ta dokument pojasnjuje pojme inkapsulacijo podatkov, prekrivanje funkcij, razredi, objekti, dedovanje, in dinamične odpremo, in kako jih izvajati v Fortran90. Kot rezultat, lahko metodologijo razviti storiti objektno programiranje v jeziku. Postscript različica tega dokumenta je na voljo tudi. I. Uvod Fortran, še vedno najbolj razširjen znanstveni programski jezik, je razvil vsakih 10 let, ali tako, da vključi najnovejša, dokazano, ideje, ki so nastale iz računalništva in programske opreme. Najnovejša različica, Fortran90 je vključena mnogo novih idej, ampak znanstvene programerji večinoma poznajo le eden izmed njiju: array sintaksa. Te druge nove ideje omogočajo boljše in varnejše programsko zasnovo. Poleg tega so lahko programske aplikacije, ki se izrazi v znanih in primerno znanstvenih konceptov. Te nove zmogljivosti v Fortran90 da znanstveni programi lažje razumeli, spreminjanje, deliti, pojasniti in razširiti. Kot rezultat, lahko veliko bolj ambiciozen programski problemi bi bili napadeni na obvladljiv način. Obstaja več lastnosti pri oblikovanju programov, ki so uporabni pri načrtovanju in vzdrževanju velikih računalniških projektov. Prvič, treba je koda, ki spreminja določeno strukturo podatkov, lokalizirajo ali omejena na eno mesto v programu in se ne širijo, nekontrolirano, v celotnem programu. Ta lastnost se imenuje inkapsulacijo. V smislu, da je posplošitev znanega pojma funkcije ali proceduro. Vendar pa sodobna enkapsulacija omogoča vse s tem povezane dejavnosti, ki se združijo okrog vrsto podatkov. Na primer, glede na vrsto podatkov, ki predstavlja logično, lahko enkapsulacija združi niz operacij, ki izvajajo logične operacije. Povezano prepričanje, da je za skrivanje informacij. Ko so zaprta vsa operacije, lahko skrijete podrobnosti, s katerimi se izračunajo rezultati, kar je samo dobro opredeljeni postopki, ki se uporabljajo za podatke. Na primer, lahko uporabimo. Ne. Delovanje na dejanski tip logično, brez skrbi, kako so pisci prevajalnika, ki se izvaja operacijo ali notranjega zastopanost logicals. Razkorak delovanje na logicals, na drugi strani, ni opredeljen v Fortran in ga zato ni mogoče uporabiti. Z zaprtjem in skrivanje informacij, uporabniki lahko opredeli svoje vrste podatkov, ki jih imenujemo abstraktni tipi podatkov. Na primer, lahko določite vrsto podatkov, ki predstavlja kota. Povzetek tipi podatkov so podrobneje opredeljeni v okviru operacije, ki se lahko uporabljajo za njih. Razred opredelitev povzema kodo za dovoljenih dejavnosti ali metode, ki jih je pisal programer skupaj z abstraktnim vrsto podatkov, ki se skriva podrobnosti izvrševanja tam. Na primer, lahko uporabniki kota tipa podatkov se uporabljajo sinus in kosinus funkcije na kotnih spremenljivk (predmetov), če so takšne metode določene za zornih kotov, vendar ne morejo razmnoževati dveh zornih kotov, če je taka metoda še ni opredeljen. Encapsulated kodo lahko varno spremeniti (na primer, drugačen način izračunavanja Sines se uporablja na novo arhitekturo), brez potrebe po spremembi kode, ki uporablja te funkcije. Druga prednost abstraktnih podatkovnih tipov je, da lahko programsko opremo oblikovalec opisuje program v smislu, podobno področju uporabe. Na primer, če je določen tip podatkov elektronov, bi se lahko opredeli, kaj pomeni potiskanje elektronskim predmetu določeno razdaljo v vesolju. (Na elektronski potisnil v tem pogledu, ker Fortran77 so posodobljeni vrednosti polj, ki predstavljajo elektronske koordinate). Nekateri postopki so opredeljeni za več kot eno standardno vrsto. Na primer, lahko se opredeli za množenje celih argumentov in dejanskih trditev, vektorji in polja. Ta lastnost se imenuje preobremenitve in razširja pojem abstraktnih podatkovnih tipov. Razmislite elektronov in ionov vrste tip, ki sta omogočali naročeno operacijo, ki se uporablja, vendar z drugačno postopek v vsakem primeru. Preobremenitev pomaga napisati krajše in jasnejše programov, ki jih bo dala splošno, v kateri se posebna sprejeti ukrep, opredeljen z vrsto argumentov pri prevajanju času. Pogosto v znanosti, lastnosti nekaterih podjetij, so odvzete in razvrščeni v razrede. Na primer, elektronov in ionov vrste se šteje za različne vrste vrst tipov. Če bi bili ločene vrste podatkov ustvarjena za elektron in ionsko bi bilo veliko dejavnosti, ki se delijo po teh dveh vrst. Preobremenitev lahko pomagajo, vendar je še bolj priročno, da uvede abstraktne vrste podatkovnega tipa za skupne lastnosti. Potem pa, kot v vsakdanjem življenju, je lahko vse, ki se uporablja za vrste, tip se uporablja tudi za elektronske in ionskih (ne pa obratno). Ko je določena vrsta podatkov vrsta mogoče poenostaviti opis ionov in elektronov vrst, ki jih dovoljuje dediščino vrste lastnosti, ki jih ti dve bolj specializirane vrste podatkov. Vse zamisli, predstavljenih kolikor so sestavni del moderne paradigme programsko imenovano objektno programiranje (OOP). Mnogi uporabniki z velikimi naložbami v Fortran77 je razlog, da ne želijo, da preidejo na zelo drugačnem slogu programskega jezika. Zahteve za časa za razvoj in usposabljanje, potrebne za prehod na nov pristop, v kombinaciji z dolgoletnimi investirali v obstoječe Fortran77 aplikacij in potrebo, da nenehno proizvaja nove znanosti so dobri razlogi, da se skeptičen eksperimentiranje z novimi idejami, kot obetaven, saj se lahko pojavijo. Fortran90 podpira te nove pomembne programske koncepte, vključno s tistimi za objektno programiranje. Ker Fortran90 je združljiva z Fortran77 je mogoče vključiti te nove ideje v starih programov v primarni način, ki omogoča, da nadaljuje znanstvenik njegove znanstvene dejavnosti. Nekatere od teh idej so koristni za tipične vrste programov pisnih posameznih avtorjev zdaj. Uporabnost druge ideje le izkaže za bolj ambiciozne programe, ki jih je napisal več avtorjev. To so programi, ki niso nikoli bili morda napisani v Fortran77, saj naj bi bil vključen kompleksnost neobvladljivi. Te nove ideje omogočajo bolj produktivno programa razvoja programske opreme spodbuja sodelovanje in pustite, da znanstvenik, ki uporabljajo iste abstraktne pojme v programu, ki so jih uporabljali tako uspešno znanstvene teorije. Znanstvena produktivnost se bo prav tako izboljšala. Poleg tega je tudi migracija pot do vzporednih računalnikih, saj je High Performance Fortran (HPF), prav tako temelji na Fortran90. V tem prispevku bomo predstavili koncepte podatkov enkapsulacija, prekrivanje funkcij, razredi in objekti, dedovanje, in dinamičnih distribucijskih v Fortran90. Ker so to temeljne gradniki objektnega programiranja, je pomembno, da jih razume, preden je mogoče učinkovito uporabiti v OOP Fortran90. Mnoge od teh idej so močni sami in so uporabne tudi brez sprejetja objektno paradigmo. Ta dokument naj bi bil uvodni v naravi. Za tiste, ki želijo opravljati te ideje še obstaja število referenc, ki razpravlja o objektno usmerjenih idej v jeziku neodvisno [1-3]. Obstaja veliko na voljo na učbeniki Fortran90 in C + +. Dva, da smo našli koristne so tiste, ki jih Ellis [4] in Lippman [5]. II. Podatki Zaprtje z zbirko predmetov Prvi koncept, bomo razpravljali, je enkapsulacija podatkov, kar pomeni, da so samo tiste postopke, ki potrebujejo dostop do nekaterih podatkov zaveda. Za ponazoritev, kako podatki inkapsulacijo deluje v Fortran90, menijo primer iz realnega v kompleksu Hitra Fourierova transformacija (FFT), napisanega v proceduro Fortran77.Vmesnik s postopkom, to je seznam argumentov in njihove vrste, se določi na naslednji način: subroutine fft1r(f,t,isign,mixup,sct,indx,nx,nxh) integer isign, indx, nx, nxh, mixup(nxh) real f(nx) complex sct(nxh), t(nxh) c rest of procedure goes here return end Tu je f matrika naj bi se preoblikovala (in izhod), t je začasno delo matrika, mixup je bit nazaj miza, SCT je sine / kosinus miza, indx je moč 2 določitev trajanja transformacija, in nx (> = 2 ** indx), je velikost f array, in nxh (= nx / 2) je velikost preostalih polj.Spremenljivka isign določa smer transformacijo (ali nič, začenja se mize mixup in SCT.) Ker je postopek fft1r zasnovan za delo z variabilnih podatkov velikosti (in bi se ločeno zbirajo) in Fortran77 ne more dinamično dodeli te podatke, se plošča in delovna miza t, SCT in mixup morala biti prijavljena v glavnem programu in se prenese vpostopek. Če postopek FFT je tudi sama vključena v notranjosti drugimi postopki, nato pa so vsi ti nizi so se prenašajo iz verige argumentov ali pa shranijo v skupni blok. Tako je lahko glavni program videti nekako takole: program main integer isign, indx, nx, nxh parameter(indx=11,nx=2**indx,nxh=nx/2) integer mixup(nxh) real f(nx) complex sct(nxh), t(nxh) c initialize fft tables isign = 0 call fft1r(f,t,isign,mixup,sct,indx,nx,nxh) stop end Cilj kapsuliranje podatkov je, da klic FFT videti takole: call fft1r(f,isign) kjer so vsi pomožni nizi in konstante, ki so potrebni le FFT skrit v FFT, preostanek programa ni treba skrbeti zanje. To skrivanje podatkov močno poenostavlja knjigovodstvo s postopki. Fortran90 omogoča dinamične nizi, ki se uporabljajo samo znotraj postopka, ki se ustvari in uniči tam, in so zato neznana izven postopka. Eden od mehanizmov za takšno inkapsulacijo je avtomatsko niz, ki se ustvari ob vstopu in uničiti pri izhodu iz postopka. To je preprosto izvajati t delovno matrike kot avtomatski matrike. Eno samo izpusti ime iz spiska argumentov: subroutine fft1r(f,isign,mixup,sct,indx,nx,nxh) integer isign, indx, nx, nxh, mixup(nxh) real f(nx) complex sct(nxh) ! t is an automatic array, it disappears on exit complex, dimension(nxh) :: t ! rest of procedure goes here end subroutine fft1r Opazimo, da smo začeli uporabljati novo Fortran 90 :: sintakso za razglasitev nizi in novo izjavo END proceduro. Avtomatski nizi razlikujejo od lokalnih nizi prej na voljo v Fortran77 saj so njihove dimenzije zdaj spremenljivke. Še en mehanizem za kapsuliranje nizi v Fortran90 je allocatable (ali z odloženim velikost) polje. So podobni avtomatskim nizi, razen da njihova ustanovitev (oblikovanje) in uničenje (deallocation) so v celoti pod nadzorom programer. Ti nizi so ustvarjene z izjavo ALLOCATE. Če je atribut SAVE uporablja v svojih izjavah, potem ne bo uničen ob izhodu iz postopka. Lahko jih izrecno uničiti s trditvijo Drugače pridijeliti. Za zdaj, vzemimo, da je tabela nizov mixup in SCT ne spreminjajo med klicih na FFT. Lahko pa jih odstranite s seznama argumentov v našem primeru, ki je izrecno dodelijo jih znotraj postopka, ko so mize inicializirana, kot sledi: subroutine fft1r(f,isign,indx,nx,nxh) integer isign, indx, nx, nxh real f(nx) ! mixup and sct are saved, allocatable arrays integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct ! t is an automatic array complex, dimension(nxh) :: t ! special initialization call if (isign.eq.0) allocate(mixup(nxh),sct(nxh)) ! rest of procedure goes here end subroutine fft1r Kasneje bomo dodali pogoje preverjanje napak. Zelo močna lastnost je, da Fortran90 nizi so dejansko paleto predmetov, ki vsebujejo ne le podatke, temveč informacije o njihovi velikosti. To je bila prej na voljo le za znakovnih polj v Fortran77, ki so dali LEN bistven za pridobitev znaka dolžino. V Fortran90 prevzela-oblika nizi so na voljo katere mere je mogoče dobiti pri velikosti resnične. To je tretja mehanizem koristen za inkapsulacijo podatkov. So prijavljeni kot običajni nizi, razen njihovih dimenzij dolžine nadomesti z dvopičjem. Sedaj lahko izpustite dimenzije NX in nxh iz spiska argumentov in jih pridobili znotraj postopka.Izjava o samodejnem t diod tako je treba revidirati, da se uporabljajo od velikosti same po sebi.Rezultat je: ! f is an assumed-shape array real, dimension(:) :: f integer :: isign, indx, nx, nxh integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct ! t is an automatic array whose size is determined from array f complex, dimension(size(f)/2) :: t ! size of arrays mixup and sct are determined from array f. nx = size(f) nxh = nx/2 ! special initialization call if (isign.eq.0) allocate(mixup(nxh),sct(nxh)) ! rest of procedure goes here end subroutine fft1r Da bi lahko uporabljali domneva, oblikovne nize mora prevajalnik pozna dejanskih argumentov vrst, ki se uporabljajo, ko se sproži postopek. Eden od načinov te informacije na voljo, je mogoče z vmesnikom izjave, ki razglaša, da je glavni program je trditev vrste postopka. Na primer, lahko zapišemo: program main integer :: isign integer, parameter :: indx=11, nx=2**indx real, dimension(nx) :: f ! declare interface interface subroutine fft1r(a,i,j) real, dimension(:) :: a integer :: i, j end subroutine fft1r end interface ! initialize fft tables isign = 0 call fft1r(f,isign,indx) stop end kjer smo uporabili novo obliko PARAMETER izjave.Dodatna prednost jasnih blokov vmesnik je prevajalnik bo zdaj pregled in zahteva, da so dejanski argumenti prenese na postopek tekmo v številu in vrsti s tistimi prijavljeni v vmesnik. Tako če pomotoma izpusti argument isign v postopku razpisa    call fft1r(f,indx) prevajalnik bo to zastavo. Ena od možnosti za napake je, da lahko pomotoma navedejo podatke v vmesnik bloku se razlikujejo od dejanskih podatkov v postopku. Ta vir napak se odstrani, če je postopek, shranjene v modulu, ki se potem ", ki se uporablja," ker je v tem primeru prevajalnik ustvari vmesnik samodejno.UPORABA stavek je podoben razširitev vključuje pogosto najdemo v Fortran77, vendar to ni besedilo zamenjave. Namesto tega daje informacije "na voljo", in je veliko močnejša od izida so. torej: module fft1r_module contains subroutine fft1r(f,isign,indx) real, dimension(:) :: f integer :: isign, indx, nx, nxh integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct complex, dimension(size(f)/2) :: t nx = size(f) nxh = nx/2 ! special initialization call if (isign.eq.0) allocate(mixup(nxh),sct(nxh)) ! rest of procedure goes here end subroutine fft1r end fft1r_module ! program main use fft1r_module ! explicit interface not needed now integer :: isign = 0 integer, parameter :: indx=11, nx=2**indx real, dimension(nx) :: f ! initialize fft tables call fft1r(f,isign,indx) stop end kjer smo uporabili nov način, da bi začeli celo število isign. Fortran90 podpira številne druge izjave in atributov, ki prispevajo k varnosti načrtovanja. Ena je IMPLICIT NONE izjavo, da zahteva, da vse spremenljivke, ki jih je treba izrecno navesti. Drug razlog pa je, da namerava atribut za trditev, da izjavi, ali so trditve namreč samo kot vhod, izhod samo, ali oboje. Če želimo navesti argumente fft1r, kot sledi: subroutine fft1r(f,isign,indx) implicit none real, dimension(:), intent(inout) :: f integer, intent(in) :: isign, indx potem spremenljivke isign in indx ni mogoče spremeniti v tem postopku, saj so bile prijavljene z namenov (IN) Na. Te funkcije pomeni, da so več napak zdaj ujela prevajalnik, ne pa operacijskega sistema, ko je koda izvaja. Zaradi tega dodane varnosti bomo uporabili module za vse preostale podprogramov v tem dokumentu. Enkapsulacija podatkov, ki smo jih kaže v tem primeru omogoča lažje za več avtorjev za samostojno razvijanje programov, ki se bodo uporabljali tudi drugi.Program FFT zdaj je preprost vmesnik, ki je manj verjetno, da je treba spremeniti, tako da tudi če avtor postopka vnaša spremembe interno uporabniki postopka ne bi bilo treba spremeniti kodo. Druga prednost takšnega pristopa je, da lahko skrijete staro, grdo kodo, ki se ne more spremeniti, morda zato, ker človek nima dostopa do vira. Na primer, če bi uporabljali knjižnice FFT, ki je optimiziran za nekatere posebne arhitekture, lahko bi ga zajame v "navidezno" postopek, ki predvideva nobenega dela ali mizo, potrebne nize in nato pokliče knjižnice FFT. Ker so podatki o knjižnici FFT skriti od uporabnika, lahko bi ga nadomestiti z drugo, s spreminjanjem le v tem "navidezno" postopek in ne vpliva na ostale kode. To omogoča, da ostanejo koda prenosni in še optimističen. Zdaj pa dodajte več preverjati napake pri dodeljevanju podatkov.Izjava dodeli omogoča neobvezno vrnitev koda napake preveri, ali so bili podatki dejansko dodeljena. In RAZPOREJENI izjava omogoča, da preveri, če so podatki že dodeljena (morda smo že uporabili FFT z različnimi podatki o dolžini). Tako je naslednja različica sredstev je varnejša: if (isign.eq.0) then if (allocated(mixup)) deallocate(mixup) if (allocated(sct)) deallocate(sct) allocate(mixup(nxh),sct(nxh),stat=ierr) if (ierr.ne.0) then print *,'allocation error' stop endif endif Eden lahko poenostavi vmesnik še z ugotovitvijo, da je dolžina FFT parameter indx potreben le v inicializacijo FFT. V naslednjih razpisih bi bilo napak, da uporabite drugačno vrednost, ne da bi pri inicializaciji novih tabel. Fortran90 podpira uporabo neobvezne argumente in notranje prisotna, da ugotovi, če je bil sprejet. S temi orodji lahko rešimo indx parameter med inicializacijo in ne zahtevajo, da se naknadno opravili. Poleg tega, če bomo shranili inicializacijo parameter indx do neke neumnosti vrednosti, ga lahko preizkusite kasneje za preprečevanje FFT bi uporabljali, preden so se inicializira FFT mize.Rezultat je: subroutine fft1r(f,isign,indx) integer, intent(in), optional :: indx integer, save :: saved_indx = -1 ! initialize to nonsense if (isign.eq.0) then if (.not.present(indx)) then print *,'indx must be present during initialization!' stop endif saved_indx = indx else if (saved_indx.lt.0) then print *,'fft tables not initialized!' stop endif endif V naslednji različici FFT, se postopek lib_fft1r nanašal na neki knjižnici, kjer FFT ali izvorna koda na voljo ali posameznik ne želijo spremeniti. Ta nova različica je veliko varnejši in lažji za uporabo in spreminjanje. subroutine fft1r(f,isign,indx) implicit none real, dimension(:), intent(inout) :: f integer, intent(in) :: isign integer, intent(in), optional :: indx integer :: nx, nxh, ierr integer, save :: saved_indx = -1 integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct complex, dimension(size(f)/2) :: t nx = size(f) nxh = nx/2 ! special initialization call if (isign.eq.0) then ! indx must be present during initialization if (.not.present(indx)) then print *,'indx must be present during initialization' stop endif ! indx must be non-negative if (indx.lt.0) then print *,'indx must be non-negative' stop endif ! save indx for future calls saved_indx = indx ! deallocate if already allocated if (allocated(mixup)) deallocate(mixup) if (allocated(sct)) deallocate(sct) ! allocate table arrays allocate(mixup(nxh),sct(nxh),stat=ierr) ! check if allocation error if (ierr.ne.0) then print *,'allocation error' stop endif ! make sure fft tables initialized else if (saved_indx.lt.0) then print *,'fft tables not initialized!' stop endif endif ! call old, ugly but fast fft here ! saved_indx used here instead of indx call lib_fft1r(f,t,isign,mixup,sct,saved_indx,nx,nxh) end subroutine fft1r Med inicializacijo bi imenovali:  call fft1r(f,isign,indx) Ampak potem se lahko uporabi samo: call fft1r(f,isign) Logično je, da smo združeni dve ločeni dejavnosti, inicializacijo in izvajanju FFT, v enem postopku. Obstaja še tretja postopek, ki bi bili koristni, deallocating notranje tabele nizov dovolj prostora, če smo storili z opravljanjem FFTs. To bi lahko dosegli z dodajanjem dodatno, neobvezne navedbe v postopku, in dodal, več kode, vendar je to neprivlačno. To naredi za boljše načrtovanje za ločitev logično ločenih postopkov v ločenih postopkov.Težava pri tem je, kako lahko različni postopki za izmenjavo dostop do notranjega tabele polja vrat mixup in SCT. V Fortran77, le mehanizem za to je bil skupni blokov. V Fortran90, da je nov mehanizem: moduli lahko vsebuje globalne podatke, ki so skupni vsem postopki v modulu, ne da bi jih izrecno izjavlja, znotraj postopkov. To je nova ideja v Fortran, čeprav je običajno v drugih jezikih, kot so C + +. Poleg tega se lahko ta globalni podatki so lokalni za modul in nedostopen za druge postopke, ki uporabljajo modul. V našem primeru bomo tabelo nizi mixup in SCT, kot tudi celo število saved_indx Global s preselitvijo izjavo izven postopka na deklaraciji oddelka na začetku modula. Prav tako bomo dodali privatni atribut, da blokira dostop do teh podatkov izven modula. Dodajanje deallocation postopka modul izgleda nekako takole: module fft1r_module ! all module procedures have access to this data integer, save, private :: saved_indx = -1 integer, dimension(:), allocatable, save, private :: mixup complex, dimension(:), allocatable, save, private :: sct contains subroutine fft1r_end ! this procedure has access to saved_indx, mixup, and sct ! reset saved_indx to nonsense value saved_indx = -1 ! deallocate table arrays deallocate(mixup,sct) end subroutine fft1r_end ! other procedures go here end module fft1r_module Z ločitvijo izvirno fft1r postopek v novo inicializacijo (fft1r_init) in FFT postopku, nam ni več treba uporabljati dodatne argumente. V končni verziji tega modula, ki je prikazan v Dodatku A, smo uporabili ";" sintakso, ki omogoča več stavkov v eno vrstico. Allocatable nizi se lahko uporablja tudi v glavnem programu, ki omogoča, da ustvarite vse nize v času izvajanja, ne pa v času prevajanja. V Fortran90, ena ni več treba prevesti kodo, ker so dimenzije spremenijo. (To je programer, odgovornost pa je, da ne uporabljate allocatable matriko, preden je bil dodeljen, ali po tem, ko je bil deallocated to.) V naslednji glavni program, naredimo f allocatable matrika, izračun vrednosti indx z vhodno napravo , dodeli f in uporabite novo sintakso polja gradbenik za inicializacijo. program main use fft1r_module implicit none integer :: indx, nx, i real, dimension(:), allocatable :: f ! write prompt without linefeed write (6,'(a)',advance='no') 'enter indx: ' ! obtain indx from input device read (5,*) indx ! allocate array f nx = 2**indx allocate(f(nx)) ! initialize data using array constructor f = (/(i,i=1,nx)/) ! initialize fft call fft1r_init(indx) ! call fft call fft1r(f,-1) ! terminate fft call fft1r_end stop end Obvestilo, da nismo spremenili prvotno lib_fft1r postopek, ki je zasebni postopek na modulu. Namesto tega smo poenostavili uporabniški vmesnik na najnujnejše in dodaja znatno varnost njegove uporabe. III. Funkcija Preobremenitev Funkcija preobremenitve se nanaša na uporabo istega ime funkcije, vendar opravljanje različnih dejavnosti, ki se nanašajo na vrsto argumentov. Fortran77 notranje funkcije in operaterji so vedno imeli to možnost. Na primer, razkorak "/" simbol daje različne rezultate, odvisno od tega, ali so operandi so cela števila, množice realnih ali kompleksnih spremenljivk. Prav tako bo bistvena REAL postopek (a) pretvori celo število za resnično, če je celo število, vendar bo dejansko del, če je zapleteno. V Fortran90, generične funkcije omogočajo uporabniško določeno funkcije, da imajo tudi to funkcijo. V primeru primer FFT, uporabniki Fortran77 morali pozabite uporabljati različna imena funkcij za vse možne vrste FFT, kot pravi na kompleksne, zapletene za kompleks, 1, 2 dimenzionalni dimenzij, enotni natančnost in zakonskih FFTs natančnosti.Generična funkcija objekt omogoča eno samo ime, na primer, FFT, ki se uporablja za vse od njih, in sicer bo prevajalnik samodejno izbere pravilno FFT za uporabo glede na število in vrsto argumentov, ki se dejansko uporabljajo. Tako je v primeru 1d realno za kompleksne FFT, smo imeli postopek z naslednjo vmesnik: subroutine fft1r(f,isign) real, dimension(:), intent(inout) :: f integer, intent(in) :: isign Na podoben način, kar je bilo opisano v prejšnjem poglavju, je mogoče sestaviti 2d resničnim za kompleksne FFT z naslednjo vmesnik: subroutine fft2r(f,isign) real, dimension(:,:), intent(inout) :: f integer, intent(in) :: isign Če je bil postopek fft2r "skriva" stara, grda, ampak hitro 2d pravi, da kompleksne FFT, ki ima veliko argumentov. V prvem primeru je f argument pravi, Enodimenzionalni matrika, medtem ko je v drugem primeru pa gre za resnično, dvodimenzionalno polje. Če sta oba od teh postopkov so v isti modul, ena gradi generično funkcijo FFT z vnosom naslednje navedbe v deklaraciji del modula: interface fft module procedure fft1r module procedure fft2r end interface Potem pa v glavnem programu, ki uporablja modul, izkaza       call fft(f,isign) bo poklical postopek fft1r, če je f realna, Enodimenzionalni matrika, ali bo poklical fft2r, če je f realna, dvodimenzionalno polje. Če je f katero koli drugo vrsto trditev, se pripravijo napaka se ustvari. Če je 2 dimenzionalni FFT v obliki posebnega modula, nato pa so potrebni dve ločeni INTERFACE izkazi. V prvem modulu 1 vključuje interface fft module procedure fft1r end interface in v drugem modulu 1 vključuje interface fft module procedure fft2r end interface Glavni program nato uporablja tako modulov, vsak modul postopek se nato doda na seznam postopkov, ki jih je splošni vmesnik FFT. Na podoben način je mogoče vključiti v istem vmesniku je vse druge vrste FFTs in programer zaščiten dela napake pri pozivu napačen postopek. Če želite, lahko celo posebno fft1r imena, fft2r nedostopen z dodajanjem izjavo:       private :: fft1r, fft2r V modulu. Prednost tega je, da se specifična imena uporabijo pri drugih modulov brez konflikta. Funkcija preobremenitve se imenuje tudi ad hoc polimorfizem. IV. Izpeljane tipov, razredov in predmetov Fortran je število bistvenih vrst podatkov, kot so celo, realno, kompleksno, logično in značaja, za katero so izvajalci in funkcije, opredeljene v jeziku.Pomembna novost na Fortran90 je uporabniško določeno vrst podatkov.Uporabniško določeno vrsto, znan tudi kot abstraktni podatkovni tip, se imenuje izpeljana vrsta v Fortran90. To je zgrajena iz notranjih vrst in vnaprej določenih tipov uporabnikov. Ena preprosta uporaba te nove zmogljivosti, je v paketih skupaj različne Skalarja, ki se običajno prenašajo skupaj kot argumenti v postopkih. Na primer, upoštevati naslednje vmesnik iz delcev, ki potiska podprograma napisan v Fortran77: subroutine push1 (part,fx,qbm,dt,ek,np,idimp,nop,nx) integer np, idimp, nop, nx real qbm, dt, ek, part(idimp,nop), fx(nx) c rest of procedure goes here return end Tu del je matrika, ki vsebuje delcev koordinate in hitrosti in FX je električno polje polje.Idimp celo število je dimenzionalnost prostora faze, nop, je največje dovoljeno število delcev in nx je velikost matrike električnega polja. Kot smo videli na primeru FFT, nimamo opraviti teh števil v Fortran90, saj se lahko določi po velikosti, če so resnični del in opravil, kot fx nizi predpostavljena-oblike.Celo np (np <= nop) je dejansko število veljavnih delcev v delu matrike, qbm je naboj / masa razmerje ek je kinetična energija np veljavnih delcev, dt je časovni korak. Vse Skalarja, razen za časovni korak opis skupine nabitih delcev in so običajno opravili skupaj, kadar so delci, ki jih obdelujejo neki postopek. Mi lahko uporabite izpeljane vrste, da jih hranite skupaj, kot sledi: type species_descriptor integer :: number_of_particles real :: charge, charge_to_mass, kinetic_energy end type species_descriptor To je podobno strukturo in rekordno vrst, ki se pojavljajo v drugih programskih jezikih. Dodali smo naboj na seznam, saj so nekateri postopki, ki prav tako zahtevajo, da. Obvestilo, da je v tej vrsti izhaja, da so sestavni del tako celo vrsto in pravi, da to ne bi bilo mogoče izvajati samo z matriko v Fortran77. Če želite ustvariti spremenljivko tega tipa, ena naredi naslednjo izjavo:    type (species_descriptor) :: electron_args, ion_args kjer smo naredili dve spremenljivke species_descriptor tipa, eno za elektronov in eno za ionov. Sestavine te nove vrste so dostopni z '%' simbol. Tako lahko določimo vrednosti na naslednji način:  electron_args% number_of_particles = 1000       electron_args% cena = 1,0 Zato je najbolje, da opredelitev v deklaraciji delu modula skupaj z novim push1 proceduro (da ne bi bilo treba izrecno izjavi, vmesnik za to), kot je prikazano v Dodatku B. Potem je ta modul "uporablja" v glavnem programu omogočiti dostop do pridobljenih vrste in nove push1 postopek, ki se zdaj imenuje z veliko preprostejši vmesnik:      call push1(part,fx,electron_args,dt) Tu smo pokazali preprosto uporabo pridobljenih vrst, le za zmanjšanje knjigovodske pri vožnji argumente postopkov. Ampak, pridobljene iz vrste so veliko močnejši od tega. Lahko se uporablja za izražanje zapletenih, abstraktne količine. V bistvu, s pridobljenimi tipi, ki jih je mogoče izraziti v načrtovanju enako visoko raven, abstraktne količine, ki se uporabljajo za fiziki v svojih matematike. Za ponazoritev, kako se bodo začeli izražati bolj zapletene matematike v programiranju, nam določiti novo vrsto private_complex in postopke, ki bodo delujejo na to vrsto. To je, seveda, akademska vaja za programerje, Fortran, saj zapletena vrsta že obstaja v jeziku. Kljub temu pa je dober primer za ponazoritev osnovnih načel, vključenih pa bo privedlo do naše opredelitve razredov. Ta vrsta je opredeljena kot sledi: type private_complex real :: real, imaginary end type private_complex Če želite ustvariti spremenljivke a, b in c tega novega tipa, in pripis vrednosti, 1 nadaljuje kot prej: type (private_complex) :: a, b, c ! assign values to a a%real = 1.0 a%imaginary = 2.0 Če je to private_complex tip obnaša enako kot navadne kompleksnih števil, potem množenje c = a * b je mogoče opredeliti kot sledi: c%real = a%real*b%real - a%imaginary*b%imaginary c%imaginary = a%real*b%imaginary + a%imaginary*b%real Nova funkcija pc_mult pomnožiti private_complex številke, lahko zapišemo: type (private_complex) function pc_mult(a,b) type (private_complex), intent(in) :: a, b pc_mult%real = a%real*b%real - a%imaginary*b%imaginary pc_mult%imaginary = a%real*b%imaginary + a%imaginary*b%real end function pc_mult Upoštevajte, da ta funkcija vrne spremenljivko private_complex tipa. Lahko torej pomnožimo dve števili te vrste z naslednjo izjavo:       c = pc_mult(a,b) Smiselno je, da dajo nov tip, ki izhaja skupaj s postopki, ki delujejo na to vrsto v isti modul: module private_complex_module ! define private_complex type type private_complex real :: real, imaginary end type private_complex contains type (private_complex) function pc_mult(a,b) ! multiply private_complex variables type (private_complex), intent(in) :: a, b pc_mult%real = a%real*b%real - a%imaginary*b%imaginary pc_mult%imaginary = a%real*b%imaginary + a%imaginary*b%real end function pc_mult end module private_complex_module Program za ponazoritev množenje dveh števil private_complex potem izgleda naslednje: program main ! bring in private_complex definition and procedures use private_complex_module ! define sample variables type (private_complex):: a, b, c ! initialize sample variables a%real = 1. ; a%imaginary = -1. b%real = -1. ; b%imaginary = 2. ! perform multiplication c = pc_mult(a,b) print *,'c=', c%real, c%imaginary stop end program main Možno je tudi, da zajame posamezne komponente izvedene vrste. To je običajno in koristno prakso v objektno programiranje. To pomeni, da ko je modul "uporablja" v drugi programske enote se lahko private_complex tip opredeljena, vendar so posamezni sestavni deli, kot so% realno ali imaginarno% niso dostopni. V vzorčnem programu zgoraj, so bili posamezni deli pogledali v inicializacijo podatkov in tiskanje rezultat množenja. Če vdelane komponente, potem bi dodatne postopke, ki jih je treba zagotoviti v modulu za opravljanje te funkcije.Enkapsulacija se doseže z dodajanjem PRIVATE atribut izpeljane opredelitev tipa, kot sledi: type private_complex private real :: real, imaginary end type private_complex Postopek za inicializacijo za private_complex številke od dejanskih številk lahko zapišemo na naslednji način: subroutine pc_init(a,real,imaginary) ! initialize private_complex variable from reals type (private_complex), intent(out) :: a real, intent(in) :: real, imaginary a%real = real a%imaginary = imaginary end subroutine pc_init medtem ko je ena prikazati lahko napisana vsebina: subroutine pc_display(a,c) ! display value of private_complex variable with label type (private_complex), intent(in) :: a character*(*), intent(in) :: c print *, c, a%real, a%imaginary end subroutine pc_display Glavni program potem izgleda naslednje: program main use private_complex_module type (private_complex) :: a, b, c ! initialize sample variables call pc_init(a,1.,-1.) call pc_init(b,-1.,2.) ! perform multiplication c = pc_mult(a,b) ! display result call pc_display(c,'c=') stop end program main Prednost takega kapsuliranje je, da postopki v drugih modulov ne more vplivati na notranjo predstavitev private_complex tipa. Poleg tega naj bi vse spremembe, narejene na notranji zastopanosti vrste private_complex biti omejena s tem modulom, in ne bi vplivalo programskih enot v drugih modulov. To omogoča lažje za razvoj programske opreme z zamenljivih delov. Smo že videli, kako je mogoče preobremenjen funkcije. V Fortran90 lahko izvajalci, kot so "*" tudi preobremenjen. To je narejeno tudi z vmesnikom izjavo, ki se daje v deklaraciji delu modula, kot sledi: interface operator(*) module procedure pc_mult end interface Sedaj smo izenačeni upravljavca '*' z imenom pc_mult. Tako se je v glavni program, je mogoče pomnožiti 2 private_complex številk, ki uporabljajo bolj znan sintakso:       c = a*b Če temu dodamo izjavo:       private :: pc_mult z modulom, lahko tudi originalno ime pc_mult ni več dostopen. V jeziku objektnega programiranja, je modul, ki smo jih pravkar ustvarili znani kot razred. Sestoji iz tega izhaja opredelitev vrste, znan kot ime razreda, skupaj s postopki, ki delujejo na tem razredu, imenovanem funkcije člana skupine. Sestavni deli, pridobljenih vrste se imenujejo izmenjava podatke razreda, medtem ko je globalni podatki v modulu (če sploh) v skladu s statičnimi razreda data članov.Dejansko spremenljivka private_complex tipa je znana kot predmet. Da bi se ta zdi bolj seznanjeni s tistimi, ki že vedo, C + +, bomo sprejeli konvencijo, da se da iz nje izhaja vnesite prvi argument v vseh modulov postopkov, in mi bo dal mu bo ime "to." Prav tako bomo preobremenjujte pc_init inicializacijske funkcije in mu javno ime "nov", pri tem pa posebno ime pc_init zasebno.Končna različica private_complex razred je naveden v Dodatku C. V tej različici smo ga podali novo javno ime "zaslon", da v postopek pc_display, nismo pa je bilo zasebno, tako da obe imeni lahko še vedno uporablja. Glavni program, ki uporablja ta razred se lahko zapiše: program main use private_complex_class type (private_complex) :: a, b, c call new(a,1.,-1.) call new(b,-1.,2.) ! perform multiplication c = a*b call pc_display(c,'c=') stop end program main V. Dediščino Videli smo, kako ustvariti preprosto pouk Fortran90 skupaj s shranjevanjem, pridobljene opredelitev vrste in postopke, ki delujejo na to vrsto, ki se potem lahko "uporablja" v drugih enotah programa, vključno z drugimi moduli. Dedovanje, lahko v najbolj splošnem pomenu, je opredeljena kot sposobnost za izgradnjo bolj kompleksnih (izpeljane) razrede od preprostejših (base) razredi v hierarhični način. Dedovanje je bil tudi kot izraz, ki opisuje, kako se je ta ideja se izvaja v določenem jeziku, kar se kaže v toliko opredelitev, kot so jeziki. Razpravljali bomo dva načina izvajanja te ideje, ki so koristne pri Fortran90, splošna 1 in bolj specializirana, vendar pogost. Začeli bomo z najbolj splošnem primeru, ko en razred uporabi drugega. Taka oblika dedovanja včasih imenujemo razred sestavi, saj so razredi v sestavi drugih razredov. Za ponazoritev tega, da nas ustvariti nov razred, ki je sestavljen iz private_complex nizi. Array sintaksa za bistvene vrste podatkov je zmogljiva nova funkcija Fortran90 in je poučno, da vidite, kako je mogoče to izvesti za tipe uporabnikov opredeljena. Fortran90 dopušča 1 za opredelitev nizov, pridobljenih vrste, kot sledi: program main use private_complex_class integer, parameter :: nx = 8 type (private_complex), dimension(nx) :: f Mi lahko inicializacijo elementov te vrste diod s klicem v zanki "nov" Postopek smo opredelili v private_complex_class: do i = 1, nx call new(f(i),real(i),-real(i)) enddo Upoštevajte, da Naredi krog brez izjava številk je zdaj uradno del Fortran90. Na ta način lahko ustvarimo postopek imenovan pcarray_init, ki bo pretvoril dva prava polja v matriki tipa (private_complex). Dajemo tega postopka v nov modul imenovan complex_array_class, kot sledi: module complex_array_class ! get private_complex type and its public procedures use private_complex_class contains subroutine pcarray_init(this,real,imaginary) ! initialize private_complex array from real arrays type (private_complex), dimension(:), intent(out) :: this real, dimension(:), intent(in) :: real, imaginary do i = 1, size(this) ! new here uses the pc_init procedure call new(this(i),real(i),imaginary(i)) enddo end subroutine pcarray_init end module complex_array_class V tem primeru je private_complex modul "uporablja" (ali zapuščino) z modulom complex_array. To pomeni, da bodo vsi javni subjekti iz prvega modula (osnova razred) na voljo drugem modulu (izpeljani razred). Ker so nizi, pridobljenih vrsti upoštevati različne vrste kot eno skalarjem te vrste, se lahko vključijo naslednjo izjavo vmesnika v modulu do preobremenitve postopek nova, tako da se lahko izvede tudi pcarray_init: interface new module procedure pcarray_init end interface Zdaj, če je argument novega je skalarna za private_complex tipa, potem pc_init treba izvesti, če pa argument je nova vrsta private_complex tipa, potem pcarray_init treba izvršiti. Glavni program, ki uporablja ta nov modul izgleda naslednje: program main ! bring in complex_array (and private_complex) procedures use complex_array_class integer, parameter :: nx = 8 ! define a single scalar of type private_complex type (private_complex) :: a ! define an array of type private_complex type (private_complex), dimension(nx) :: f ! initialize a single scalar call new(a,1.0,-1.0) ! initialize an array call new(f,(/(real(i),i=1,nx)/),(/(-real(i),i=1,nx)/)) stop end program main Na podoben način lahko dodamo pomnožimo funkcijo novega modula, ki bo razmnoževala nize. Če želite to narediti moramo izkoristiti novo funkcijo za Fortran90, ki je, da se lahko vrnejo funkcije celotne nize. To se naredi z razglasitvijo ime funkcije, da je matrika, in določa njeno razsežnost iz drugega niza, v spisek argumentov z velikostjo resnične. Ker je "*" operater za Skalarja na private_complex vrsti že opredeljena v modulu private_complex_class je matrika funkcija ustvarjena tako, da zahteva ta operator v zanko, kot sledi: function pcarray_mult(this,b) ! multiply arrays of private_complex variables type (private_complex), dimension(:), intent(in) :: this, b ! declare output array to be of same size as "this" array type (private_complex), dimension(size(this)) ::pcarray_mult do i = 1, size(this) ! this multiplication is actually performed by function pc_mult pcarray_mult(i) = this(i)*b(i) enddo end function pcarray_mult Končno bomo lahko preobremenijo '* "upravljavca zahtevati pcarray_mult ko argumenti so nizi private_complex tipa, kot sledi: interface operator(*) module procedure pcarray_mult end interface V končni različici izpeljane complex_array razred, ki je naveden v dodatku D, smo prav tako preobremenjene funkcije prikazovanja, tako da lahko natisnete matrične elemente. V naslednji glavni program, lahko sedaj pomnožimo nizov private_complex tipa z matrično sintakso: program main use complex_array_class integer, parameter :: nx = 8 ! define sample arrays type (private_complex), dimension(nx) :: f, g, h ! initialize sample arrays call new(f,(/(real(i),i=1,nx)/),(/(-real(i),i=1,nx)/)) call new(g,(/(-real(i),i=1,nx)/),(/(real(2*i),i=1,nx)/)) ! perform multiplication of arrays h = f*g ! display the first three elements of the results call display(h(1:3),'h=') stop end program main Tako smo zgradili izpeljana razreda, ki temelji na opredelitvah in postopkih v osnovnem razredu z vrhunsko sestavo. Ni bilo potrebno zgraditi novo pridobljene tip za polja, saj so nizi lesnih vrst samodejno podporo v izbranem jeziku. Postopki, ki delujejo na polja, pa je bilo treba konstruirati. To je bilo narejeno v dveh fazah. Najprej je nov postopek za izpeljane razreda pisno (na primer pcarray_mult), ki izvaja prvotno operaterja osnovnega razreda ("*"), v krogu. Prvotna osnova razred subjekt ("*"), nato pa preobremenjeni, da se vključi nov postopek izpeljana razreda. Upoštevajte, da je bil osnovni razred nikoli spremenil med tem postopkom. Dedovanje se običajno uporablja za nekaj pomeni bolj omejen kot razredne kompozicije. V tej obliki dediščino zvezi, osnova razred vsebuje lastnosti (postopke), ki so skupna za določeno skupino, ki izhajajo razredov. Vsak izpeljan razred lahko spremeni ali razširi te postopke za lastne potrebe, če je to potrebno. Kot primer, menijo, private_complex razred razpravljali v prejšnjem poglavju. Recimo, da želimo, da se ta razred, tako da beleži opravljene zadnje operacije. Takšna možnost bi lahko bila koristna pri odpravljanju napak, na primer. Razen za dodatno funkcijo spremljanja poslovanja, želimo razširiti ta razred se obnašajo točno tako kot private_complex razreda. Lahko bi to dosegli z oblikovanjem novega razreda imenovano monitor_complex razred. Najprej smo ustvarili novo pridobljeno vrsto, kot sledi: type monitor_complex type (private_complex) :: pc character*8 :: last_op end type monitor_complex ki vsebuje en primerek vrste private_complex dodatnemu znakov komponente, ki se uporablja za spremljanje. Želimo razširiti vse tri postopke za private_complex razreda (nov, "*", ter izpis) tako, da deluje tudi v novem razredu monitor_complex. Mi bomo to dosegli z metodami, zelo podobne tistim, ki jih uporabljajo v sestavi. Inicializacija monitor_complex tipa se lahko izvede z uporabo novega upravljavca v private_complex razred inicializirati private_complex komponento monitor_complex tipom, kakor sledi: type (monitor_complex) :: x call new(x%pc,1.0,-1.0) Dodatni monitor element lahko inicializiramo na običajen način:       x%last_op = 'INIT' To inicializacijo lahko vgradite v postopku, imenovanem mc_init. Z dodajanjem tudi nov vmesnik za monitor_complex razredu, lahko preveč nov postopek, tako da zahteva mc_init če argument je monitor_complex tipa. Zaradi tega je bil izvajalec nova, ki je prej delovala na vrsto private_complex razširiti tudi delo na monitor_complex vrste, kot je potrebno.Kar monitor_complex razred izgleda nekako takole: module monitor_complex_class ! get (inherit) private_complex type and its public procedures use private_complex_class ! define monitor_complex type type monitor_complex private type (private_complex) :: pc character*8 :: last_op end type monitor_complex interface new module procedure mc_init end interface contains subroutine mc_init(this,real,imaginary) ! initialize monitor_complex variable from reals type (monitor_complex), intent(out) :: this real, intent(in) :: real, imaginary ! initialize private_complex component of monitor_complex call new(this%pc,real,imaginary) this%last_op = 'INIT' ! set last operation end subroutine mc_init end module monitor_complex_class Na podoben način se lahko razširi množenje funkcijo deluje tudi na monitor_complex vrst. Razmnoževanje se izvaja z uporabo '*' operaterja (ki je v private_complex razred) na private_complex komponente monitor_complex vrste, kot sledi: type (monitor_complex) :: x, y, z z%pc = x%pc*y%pc To operacijo je mogoče vgraditi v funkciji, ki vrne rezultat monitor_complex tipa. Prav tako želimo dodati to funkcijo delovanja nastavitev monitorja komponento.Kar Postopek se lahko zapiše: type (monitor_complex) function mc_mult(this,b) type (monitor_complex), intent(in) :: this, b mc_mult%pc = this%pc*b%pc mc_mult%last_op = 'MULTIPLY' end function mc_mult Končno bomo lahko preobremenijo '*' operaterja z vmesnikom izjavo, da je zahteva mc_mult ko argumenti so monitor_complex tipa. V končni različici izpeljane monitor_complex razred, ki je naveden v dodatku E, smo razširili funkcije prikazovanja za delo z monitor_complex razreda, kot tudi napisal popolnoma nov postopek (last_op), da prikažete zadnjo opravljeno operacijo. Da bi lahko izrazi z mešanimi tipi smo tudi napisal mc pretvorbe izvajalca za pretvorbo iz private_complex na monitor_complex vrste. V naslednji glavni program, so bili vsi postopki v razredu osnove razširi na delo v izpeljane razreda: program main ! bring in monitor_complex definition and procedures use monitor_complex_class ! define sample variables type (private_complex) :: a, b, c type (monitor_complex) :: x, y, z ! initialize private_complex variables call new(a,1.,-1.) call new(b,-1.,2.) ! initialize monitor_complex variables call new(x,1.,-1.) call new(y,-1.,2.) call new(z,0.,0.) ! perform multiplication with private_complex types c = a*b ! perform multiplication with monitor_complex types z = x*y ! display results call display(c,'c=') call display(z,'z=') ! perform multiplication with mixed types z = mc(c)*z ! display last operation for monitor_complex type call last_op(z,'z') end program main Tako smo zgradili izpeljana razreda, ki ima posebno obliko odnosa do svojega osnovnega razreda. Tu izpeljana vrsta opredelitev v izpeljane razreda znotraj vsebuje natanko eno komponento izhaja opredelitev tipa v baznem razredu. Z drugimi besedami, vsak objekt izpeljane razreda znotraj vsebuje natanko en predmet osnovnega razreda. Poleg tega so bili vsi postopki v baznem razredu razširiti tudi delo v razredu izpeljana, čeprav so bile notranje spremembe 2 in 1 nova ustvarjena. Poleg tega je na voljo pretvorba operater. Razširitev postopkov je bilo izvedeno tako, da najprej pisno izpeljana razreda postopek, ki se imenuje postopek osnovnega razreda na osnovni sestavni del razreda, nato pa preobremenitev ime je enako imenu razreda osnovne postopku. Kot v sestavi razredu, je bil osnovni razred ni spremenil med tem postopkom. Ta oblika dedovanja se včasih imenuje subtyping, saj lahko nastanejo predmeti razreda uporabljajo postopke razreda osnovne kot če bi bile iste vrste. Čeprav je dedovanje, ki jih subtyping je precej omejen, je tudi zelo priročen, saj posebni mehanizmi, ki so v nekaterih jezikih (kot so C + +) samodejno podaljša nespremenjene postopke razredu osnovne tipe, da izhajajo z avtomatsko pretvorbo tipov. Fortran90 nima takšne mehanizme in s tem dedovanjem subtyping mora biti jasno izdelane, ki je lahko težavno. V številnih objektno usmerjenih jezikov, sestava in subtyping obravnavati ločeno, ker so izvajali dva različna jezika mehanizmov in sestava se redko upoštevajo obliko dedovanja. Vendar pa je v Fortran90 je subtyping izvaja kot poseben primer sestavo in je izdelan z uporabo podobnih metod, tako da je smiselno tukaj upoštevati dve zamisli so povezane in jih je treba opisati skupaj. Po naših izkušnjah je sestava močnejši ideja, ki pridejo bolj do izraza kot subtyping, kako so koncepti izdelana iz fizike. Ta primer s private_complex vrst kaže, eden od razlogov, objektno programiranje je tako močna: če se odločimo za spremembo notranje zastopanost private_complex za uporabo polarnih koordinat namesto kartezični, bi morali spremeniti samo postopke v razredu private_complex za knjiženje spremembe, medtem ko bi bili izpeljani razredi še vedno deluje brez sprememb. Ta primer je morda akademsko. Vendar pa se lahko uporabljajo enake tehnike za ustvarjanje bolj učinkovite in zanimive tečaje za zastopanje druge vrste algebras. Na primer, lahko ustvarite vektorske razrede s postopki, kot so strmine, subjektov ali tenzorja razredov in z njimi povezanimi postopki. Eden lahko potem program na enako visoki ravni, da je mogoče narediti matematiko, z vso močjo in varnost take abstrakcije dal. VI. Izpeljane Vrste z kazalci V species_module ustvarili prej (navedena v Prilogi B), smo napisali novo Potprogram push1, ki je imel z naslednjimi argumenti:       subroutine push1(part,fx,species_args,dt) Ta argument je bil species_args primer species_descriptor tipa, ki vsebuje določene parametre, ki opisujejo skupino nabitih delcev.Matrika del vsebuje delcev koordinate za to skupino. To deluje dobro in podprogramov vmesnik precej poenostavljen od prvotne različice. Kljub temu je mogoče dodati še večjo varnost in preprostost. Jasno je, da matrika del mora imeti objekt species_args vedno prisotna. To vodi do morebitnega vira napak, saj je mogoče opraviti deskriptorja, ki je v nasprotju z delci podatkov. Smiselno je torej, da bi nov izvedeno vrste, ki združuje dve vrsti podatkov skupaj. Lahko bi opredelitev takšne vrste, kot sledi: type species type (species_descriptor) :: descriptor real, dimension(idimp,nop) :: coordinates end type species Če idimp in nop so parametri znani ob prevajanju časa, kot je opredelitev popolnoma veljavna. Vendar pa ni primerna, saj spreminja idimp ali nop bi bilo treba velik del kode, ki se prevesti. Lahko bi verjetno, da bi bilo bolje, da imajo allocatable polje v vrsti opredelitve, kot so: type species type (species_descriptor) :: descriptor real, dimension(:,:), allocatable :: coordinates end type species Izkazalo se je, da je ta veljaven: allocatable nizi se ne sme uporabljati v pridobljenih opredelitev vrste. Obstaja možnost, vendar, ki je veljavna, in sicer kazalci nizi. Da bi pojasnila, kako to deluje, moramo narediti odmik in razloži, kako Fortran90 kazalci delo. Kazalci v katerem koli jeziku, se nanašajo na lokacijo v pomnilniku podatkov, namesto podatkov sam. Fortran90 kazalci so v resnici posebna vrsta objekta. V primerjavi s kazalci v drugih jezikih, je njihova uporaba zelo omejena, da bi se izognili vrsto degradacije delovanja skupnega z uporabo indiscriminant kazalcev.Programer nima dostopa do vrednosti kazalca niti kazalec aritmetična dovoljeno. Namesto tega kazalci so res vzdevki do drugih podatkov. Recimo, da smo opredelili dva prava polja in 2 kazalci na realnih matrik: real, dimension(4), target :: a, b real, dimension(:), pointer :: ptr1, ptr2 Posameznik se lahko nato poveže prvi kazalec s polja, z '=> "upravljavca, kakor sledi:       ptr1 => a Obvestilo, da mora biti cilj atribut uporabljen za polja in b, da bi jih lahko izpostavil.Vrste podatkov, ki jih je mogoče v zvezi kazalec je določen v izjavi. Tako lahko kazalec ptr1 le opozarjajo na resnične, 1 dimenzionalni nizi in ne more kazati na prave, 2 dimenzionalni array, na primer. Ko so povezana s ciljem, lahko kazalec pa se uporabljajo samo, kot da je matrika, vključno z njegovo posredovanje kot argument postopka. To se imenuje Dereferenciranje. Na primer izjava       ptr1(1) = 1.0 bo dodelil vrednost 1,0 na (1).Izničili neločljivo se uporablja za loči od kazalec na matriko:       nullify(ptr1) Podobno je, če bomo povezali z drugo kazalec b,       ptr2 => b potem izjava     ptr2(2) = 2.0 bo dodelil vrednost 2,0 B (2).POVEZANI notranja funkcija se lahko uporablja za ugotavljanje, če je bil kazalec povezan z nobenim niz:      if (associated(ptr1)) print *,'ptr1 associated!' Prav tako se lahko uporablja za ugotavljanje, če se dve kazalci kažejo na isto lokacijo v pomnilniku.       if (associated(ptr1,ptr2)) print *,'associated together!' Za naše namene, najbolj uporabna funkcija namig je, da jih lahko povezano z neimenovano spremenljivko z izjavo ALLOCATE. Na primer, izjave nullify(ptr1) allocate(ptr1(4)) najprej ločiti ptr1 s katerokoli prejšnjo array, nato pa dodeli neimenovanega paleto sestavljajo 4 pravih besed in povezali kazalca ptr1 ta neimenovani matrike.Kazalec ptr1 se lahko nato uporabijo kot da bi bila normalna matrika. Ko smo končali s tem polju, lahko to Drugače pridijeliti z:       deallocate(ptr1) RAZPOREJEN notranja ne deluje s kazalci na poljih. Vendar pa se lahko s tem povezano izjavo nam pove, ali so bili podatki dejansko dodeljena, če najprej izničili ptr1 pred dodelitvijo. Za vse praktične namene, kazalci na neimenovane nizi deluje tako kot allocatable nizi, le da imajo to prednost, da se lahko uporabljajo v pridobljenih opredelitev vrste. Tako je pravilna opredelitev za novo vrsto vrste je: type species type (species_descriptor) :: descriptor real, dimension(:,:), pointer :: coordinates end type species Če želimo opredeliti predmet tipa vrste pozval elektrone,       type (species) :: electrons potem se lahko kazalec polje za delce podatkov razdeli, kot sledi:       allocate(electrons%coordinates(idimp,nop),stat=ierr) Nova push1 postopek se lahko zdaj imenuje s preprostim stavkom:       call push1(electrons,fx,dt) To je mogoče učinkovito organizirati tako, da ustvarite dva razreda. Najprej smo ustvarili species_descriptor razred (glej Dodatek F), ki vsebuje vrsto opredelitev za to skupino, skupaj s postopki za branje in pisanje cilje tega razreda. Ta razred se nato "podedoval", ki ga izpeljana razreda, imenovanih vrst (glej prilogo G), ki uporablja podatke osnovnega razreda, da se opredeli vrsta tip skupaj s postopkom za odprtje in novo push1 proceduro. Obstaja ena subtilno vprašanje, ki se pojavlja pri uporabi pridobljenega iz vrst, ki vsebujejo napotke. Ko se kopirajo dve taki vrsti: type (species) :: electrons, ions ions = electrons kaj se dejansko zgodi, je ions%descriptor = electrons%descriptor ions%coordinates => electrons%coordinates V drugi vrstici je kazalec del kopirati, ne da podatki, ki jih navaja. Včasih to ni zaželeno obnašanje. Če želimo kopirati podatke namesto kazalca, moramo izvesti naslednje operacije namesto:       ions%coordinates = electrons%coordinates ki bo dereference kazalec in kopiranje podatkov. Da bi to dosegli, moramo oblikovati postopek: subroutine copy_species(a,b) type (species), intent(out) :: a type (species), intent(in) :: b a%descriptor = b%descriptor a%coordinates = b%coordinates end subroutine copy_species Fortran90 omogoča tak postopek, da se vključi v "=" operater s pomočjo vmesnika izjavo v modulu: interface assignment (=) module procedure copy_species end interface Če želimo izvesti tak postopek, potem izjava:       ions = electrons bo prepisal podatke, namesto kazalca. VII. Dynamic Razporejanje En koncept nismo razpravljali, če je ideja dinamične dispečiranja, ki se včasih imenuje doživljenjsko polimorfizem, ki je pogosto dejal, da je značilnost objektnega programiranja.Subtyping dediščino model, smo se pogovarjali v oddelku V bila statična, kar pomeni, da bi lahko rešili prevajalnik, kateri postopek za klic v danem programu. Namen dinamično razporejanje je, da omogoči eni pisati splošne ali abstraktne postopkov, ki bi se ukvarjali z vseh razredov v dediščino hierarhije, vendar pa dosegajo rezultate, ki so odvisne od, ki je bila predmet dejansko uporabljala v času izvajanja. Dinamično razporejanje je najbolj koristno, če obstaja veliko predmetov, ki so podobni drug drugemu, vendar ne povsem enako, in ki se lahko obdelajo z nekaterimi splošnimi postopki.Običajen primer pride pri predelavi baze [8], kjer obstaja veliko podobnih vrst evidenc, študentov in učiteljev, na primer. Eden želi izogniti pisanju različnih programov za vsako vrsto zapisa, vendar nihče noče za ravnanje z vsako vrsto zapisa drugače. Za ponazoritev, nam napišite podprogram, ki pa kakšna dela z uporabo metod v naši hierarhiji private_complex razreda. Ker je to dedovanje hierarhija je bila precej preprosta, bo naše delo podprogram tudi preprost: to bo kvadratni številko in nato natisnete rezultat subroutine work(a) type (private_complex), intent(inout) :: a a = a*a call display(a,Õwork:Õ) end subroutine work Opredelili bomo prikazni postopek, tako da deluje drugače v vsakem razredu (na primer s spremembo prikaza postopek v monitor_complex razredu našteti v dodatku E, tako da zahteva last_op).Delo podprogram je napisan za private_complex tipa, vendar želimo, da delujejo pravilno, tudi če se peljemo z monitor_complex vrsto namesto (kar je običajno vrste kršitev). Z drugimi besedami, če rečemo, da ta postopek deluje na private_complex vrste, smo dejansko pomeni, da je moral delati na vseh vrstah, ki izhajajo iz private_complex kot dobro. Poleg tega želimo, da odloča v času izvajanja, ki mislimo. Torej, če se jih razglasi za inicializacijo razred predmetov, kot sledi: type (private_complex), pointer :: pc type (private_complex), target :: a type (monitor_complex), target :: b ! initialize private_complex variables call new(a,1.,-1.) ! initialize monitor_complex variables call new(b,1.,-1.) želimo narediti nekaj takega: ! point to private_complex variable pc => a call work(pc) ! point to monitor_complex variable pc => b !error, type violation call work(pc) To ni mogoče Fortran90, saj lahko le kazalec pc kažejo na ciljih private_complex tipa, tako da je izjava:    pc => b nezakonita.Rešitev je, da se najprej določi izpeljana vrsta, ki vsebuje kazalec na vseh možnih vrst v dediščino hierarhije, kot so: type complex_subtype type (private_complex), pointer :: pc type (monitor_complex), pointer :: mc end type complex_subtype Ta podvrsta, ima možnost, da kaže na vsakem kompleksnega tipa: type (complex_subtype) :: generic_pc ! point to private_complex variable generic_pc%pc => a ! point to monitor_complex variable generic_pc%mc => b Dinamično razporejanje se nato izvajajo z opredelitvijo podtipa razred, ki podeduje vse razrede v dediščino hierarhije in vsebuje novo različico vsake funkcije člana razreda. Ta nova različica bo preveril, katera od kazalcev v complex_subtype tip je povezan in izvajajo ustrezno različico funkcije. Na primer, lahko definiramo nov postopek zaslona, kot sledi: subroutine display_subtype(a,c) type (complex_subtype), intent(in) :: a character*(*), intent(in) :: c ! check if pointer is associated with private_complex type if (associated(a%pc)) then ! if so, execute private_complex version of display call display(a%pc,c) ! check if pointer is associated with monitor_complex type elseif (associated(a%mc)) then ! if so, execute monitor_complex version of display call display(a%mc,c) endif end subroutine display_subtype Trditev, da tega postopka je spremenljivka complex_subtype tipa.Postopek skriva odločitev o tem, katere dejanska funkcija za klic. Na podoben način, bi lahko napisali novo funkcijo multiplikator, ki bi skrili odločitev o ustreznem postopku množenja za klic. Če ena preobremenitve se postopek imena, da imajo ista imena kot ustrezne funkcije člana razreda, kar dela Postopek se nato popolnoma enako obliko kot prej, le da je trditev complex_subtype tipa namesto private_complex, kar pomeni, da je namenjen ta podprogram , ki se uporabljajo s celotno hierarhijo razreda: subroutine work(a) type (complex_subtype), intent(inout) :: a ! multiplication operator has been overloaded to cover all types a = a*a call display(a,'work:') end subroutine work Iz zgoraj navedenega je jasno, da bo podtip razred potrebujejo dodelitvi operaterja. Ker bomo sestavne dele complex_subtype razreda zasebni, ena je, da napišete postopek za dinamično dodeli eno od možnih kazalcev v razredu hierarhije in odpravile vse ostalo. Na primer, Razporeditev private_complex kazalec mora opraviti: subroutine assign_pc(cs,pc) type (complex_subtype), intent(out) :: cs type (private_complex), target, intent(in) :: pc ! assign private_complex to complex_subtype cs%pc => pc ! nullify monitor_complex pointer nullify(cs%mc) end subroutine assign_pc Podoben postopek, ki smo ga poimenovali assign_mc, je treba ustvariti dodeliti monitor_complex kazalec. Končno, bi po naslednji formuli:       a = a*a do želenih rezultatov, eden mora tudi določiti novega najemnika kopiranje, copy_subtype, ki kopira podatke namesto kazalce, podobno tisti, ki smo se določi ob koncu oddelku VI. Ti operaterji razvrščanje in izvod lahko preobremenjen z podeljevanja operaterja ("="). The complex_subtype razred, ki združuje vse te funkcije je prikazan v dodatku H. To povzema vse podrobnosti o tem, kako dinamičnih distribucijskih del, tako da lahko uporabniki tega razreda prosto pisati abstraktnih ali generični postopke na podlagi razredne hierarhije da bi se zavedali, kako dinamično razporejanje se izvaja. Program, ki zahteva delo izgleda nekako takole: program main use complex_subtype_class type (complex_subtype) :: pc type (private_complex), target :: a type (monitor_complex), target :: b call new(a,1.,-1.) call new(b,1.,-1.) call assign(pc,a) call work(pc) call assign(pc,b) call work(pc) end program main Posebnost tipkanih objektno usmerjenih jezikov, je, da je podpora za enakovredno takega razreda podtipa se samodejno podpira prevajalnik. Vedno je uspešnost kazen za uporabo dinamičnih dispečiranja, pa tudi v objektno usmerjenih jezikov. Pravila za izvajanje podtipa razreda v Fortran90, ki podpira dinamično razporejanje so: ustvarjanje izhaja vrsta, ki vsebuje natanko en kazalec za vsako možno skupino v dediščino hierarhije. Potem izvajati splošno metodo za vsako funkcijo člana razreda, ki bo preveril, katera od možnih kazalcev so bili povezani in prenesti ustrezen kazalec na ustrezno funkcijo. Odstop postopki so potrebni tudi. Uporabniki podtipa razreda, vendar ni treba, da se ukvarja s podrobnostmi o tem, kako je zgrajen. Jasno je, da je bolj nerodno izvajati dinamično razporejanje v Fortran90 kot v objektno usmerjen jezik. Po izvedbi pa je uporaba podobna. Ali je ta funkcija pomembna je odvisna od narave problema 1 hoče model. Nekatere študije objektnega programiranja [9] kažejo, da se dinamika pošiljanje potrebno približno 15% časa. Obstaja nekaj razprave o tem, kaj pomeni objektno programiranje. Nekateri trdijo, da bi program ne uporabljajo dinamično odpremo, ni objektno usmerjen. Drugi pa trdijo, da je objektno usmerjen, je vprašanje modela, ne gre za vprašanje, kaj ima za objektno usmerjen jezik uporabljajo. Nagnjeni smo k temu, da se strinjam s tem namenom. VIII. Sklepi Fortran90 je sodoben, zmogljiv jezik. Še veliko več pa tukaj kot sintakso polja, čeprav je to koristno. Veliko pomembnih programskih konceptov so podprti, kot kapsuliranje podatkov, prekrivanje funkcij in uporabniško definirane tipe. Jezik podpira tudi številne varnostne značilnosti, kot so trditve, po postopkih preverjanja, implicitno ni, in namen lastnosti, ki omogočajo prevajalnik, da bi našli veliko napak programiranja. Čeprav Fortran90 ne šteje objektno usmerjen jezik, lahko metodologijo razviti storiti objektno programiranje. Podrobnosti o oblikovanju objektno usmerjenih programov, so izven obsega te uvodnem članku, ampak so uspešno pisali in v primerjavi Fortran90 in C + + različice objektno usmerjen v plazmi delcev-in-celic program [6-7]. Toda tudi če ne bi želel prevzeti celotno objektno paradigmo, programski koncepti so zelo koristne tudi, če uporabljajo selektivno. To je mogoče oblikovati preprosta, varnejši uporabniške vmesnike za skrivanje stare, grde kazenski zakonik, ki še vedno lahko delajo zelo dobro. Priznanja: Raziskave Viktor K. Decyk je bila izvedena v okviru na UCLA in je potekala pod pokroviteljstvom USDOE in NSF. Prav tako je izvedla delno na Jet Propulsion Laboratory, California Institute of Technology, v skladu s pogodbo z Nacionalnim aeronavtiko in vesolje Administration. Raziskave Charles D. Norton je podprl Študentski NASA podiplomski raziskovalcev v okviru donacije NGT-70334, in z dne Boleslaw K. Szymański je delno podprla NSF na podlagi nepovratnih sredstev CCR-9527151. Zavedamo prispevek Steve Lantz, naše prijazno "in-house" pregledovalec, katerih vprašanja in pripombe izboljšala papir. Reference:
  1. Michel Beaudouin-Lafon, objektno usmerjenih jezikov, v prevodu Jack Howlett [Chapman & Hall, New York, 1994].
  2. James Rumbaugh, Michael Blaha, William Premerlani, Frederick Eddy, in William Lorensen, Object-Oriented Modeling and Design [Prentice-Hall, Englewood Cliffs, NJ, 1991]
  3. Kathleen Fisher in John C. Mitchell, "opozarja na tipkano objektno programiranje," v teoretični vidiki programske opreme računalnikov, Proc. Mednarodne simpozija CELOTNIH DOVOLJENIH ULOVIH '94, Sendai, Japonska, april, 1994, ed. M. Hagiya in JC Mitchell [Springer-Verlag, Berlin, 1994], str. 844.
  4. TMR Ellis, Ivor R. Philips in Thomas M. Lahey, Fortran 90 Programiranje [Addison-Wesley, Reading, Massachusetts, 1994].
  5. Stanley B. Lippman, C Primer, [Addison-Wesley, Reading, Massachusetts, 1991].
  6. Charles D. Norton, Boleslaw K. Szymański in Viktor K. Decyk, "Object-Oriented Vzporedno Izračun za simulacijo plazma," Sporočila ACM, vol. 38, št. 10, str. 88 (1995).
  7. Charles D. Norton, Viktor K. Decyk in Boleslaw K. Szymański "Na Parallel objektnega programiranja v Fortran90," ACM SIGAPP Applied Computing mnenje, vol. 4, št. 1, str. 27, 1996.
  8. R. Henderson in B. Zorn, "Primerjava objektno programiranje v štirih moderne jezike," Programska oprema prakse in izkušnje, Vol. 24, Št. 11, pp 1077-1095, november 1994.
  9. Grady Booch, objektno orientirana analiza in dizajn [Benjamin / Cummings, Redwood City, CA, 1994], str. 120.
Dodatek: Končna različica fft1r_module module fft1r_module integer, save, private :: saved_indx = -1 integer, dimension(:), allocatable, save, private :: mixup complex, dimension(:), allocatable, save, private :: sct contains subroutine fft1r_init(indx) ! initialization call integer, intent(in) :: indx integer :: nx, nxh, ierr, isign=0 ! allocate f and t: old, ugly fft requires them as arguments real, dimension(2**indx) :: f complex, dimension(2**(indx-1)) :: t if (indx.lt.0) then print *,'indx must be non-negative' stop endif nx = 2**indx ; nxh = nx/2 ; saved_indx = indx if (allocated(mixup)) deallocate(mixup) if (allocated(sct)) deallocate(sct) allocate(mixup(nxh),sct(nxh),stat=ierr) if (ierr.ne.0) then print *,'allocation error' stop endif ! call old, ugly but fast fft here call lib_fft1r(f,t,isign,mixup,sct,saved_indx,nx,nxh) end subroutine fft1r_init ! subroutine fft1r_end ! deallocate internal data saved_indx = -1 deallocate(mixup,sct) end subroutine fft1r_end ! subroutine fft1r(f,isign) real, dimension(:), intent(inout) :: f integer, intent(in) :: isign integer :: nx, nxh complex, dimension(size(f)/2) :: t nx = size(f) ; nxh = nx/2 ! do nothing if isign is invalid if (isign.eq.0) return if (saved_indx.lt.0) then print *,'fft tables not initialized!' stop endif ! call old, ugly but fast fft here call lib_fft1r(f,t,isign,mixup,sct,saved_indx,nx,nxh) end subroutine fft1r end module fft1r_module Dodatek B: Nova različica species_module module species_module ! define derived type type species_descriptor integer :: number_of_particles real :: charge, charge_to_mass, kinetic_energy end type species_descriptor contains subroutine push1(part,fx,species_args,dt) ! declare assumed-shape arrays real, dimension(:,:), intent(inout) :: part real, dimension(:), intent(in) :: fx ! declare argument of derived type type (species_descriptor), intent(inout) :: species_args real, intent(in) :: dt integer :: np, idimp, nop, nx real :: qbm, ek ! extract array sizes idimp = size(part,1) ; nop = size(part,2) ; nx = size(fx) ! unpack input scalars from derived type qbm = species_args%charge_to_mass np = species_args%number_of_particles ! call old, ugly but fast particle pusher here call orig_push1(part,fx,qbm,dt,ek,np,idimp,nop,nx) ! pack output scalars into derived type species_args%kinetic_energy = ek end subroutine push1 end module species_module Dodatek C: Končna različica razreda private_complex module private_complex_class private :: pc_init, pc_mult type private_complex private real :: real, imaginary end type private_complex interface new module procedure pc_init end interface interface operator(*) module procedure pc_mult end interface interface display module procedure pc_display end interface contains subroutine pc_init(this,real,imaginary) ! initialize private_complex variable from reals type (private_complex), intent(out) :: this real, intent(in) :: real, imaginary this%real = real this%imaginary = imaginary end subroutine pc_init ! type (private_complex) function pc_mult(this,b) ! multiply private_complex variables type (private_complex), intent(in) :: this, b pc_mult%real = this%real*b%real - &this%imaginary*b%imaginary pc_mult%imaginary = this%real*b%imaginary + &this%imaginary*b%real end function pc_mult ! subroutine pc_display(this,c) ! display value of private_complex variable with optional label type (private_complex), intent(in) :: this character*(*), intent(in), optional :: c if (present(c)) then print *, c, this%real, this%imaginary else write (6,'(2f14,7)',advance='no') this%real, &this%imaginary endif end subroutine pc_display end module private_complex_class Dodatek D: Končna različica complex_array razred module complex_array_class use private_complex_class private :: pcarray_init, pcarray_mult interface new module procedure pcarray_init end interface interface operator(*) module procedure pcarray_mult end interface interface display module procedure pcarray_display end interface contains subroutine pcarray_init(this,real,imaginary) ! initialize private_complex variable from reals type (private_complex), dimension(:), intent(out) :: this real, dimension (:), intent(in) :: real, imaginary do i = 1, size(this) call new(this(i),real(i),imaginary(i)) enddo end subroutine pcarray_init ! function pcarray_mult(this,b) ! multiply arrays of private_complex variables type (private_complex), dimension(:),intent(in) :: this,b type (private_complex), dimension(size(this)):: &pcarray_mult ! this multiplication is actually defined by function pc_mult do i = 1, size(this) pcarray_mult(i) = this(i)*b(i) enddo end function pcarray_mult ! subroutine pcarray_display(this,c) ! display value of private_complex array with label type (private_complex), dimension(:), intent(in) :: this character*(*), intent(in) :: c write (6,'(a)',advance='no') c do i = 1, size(this) call display(this(i)) enddo print * end subroutine pcarray_display end module complex_array_class Dodatek E: Končna različica razreda monitor_complex module monitor_complex_class ! get (inherit) private_complex type and its public procedures use private_complex_class private :: mc_init, mc_mult, mc_display ! define monitor_complex type type monitor_complex private type (private_complex) :: pc character*8 :: last_op end type monitor_complex interface new module procedure mc_init end interface interface operator(*) module procedure mc_mult end interface interface display module procedure mc_display end interface contains subroutine mc_init(this,real,imaginary) ! initialize monitor_complex variable from reals type (monitor_complex), intent(out) :: this real, intent(in) :: real, imaginary ! initialize private_complex component of monitor_complex call new(this%pc,real,imaginary) ! set last operation this%last_op = 'INIT' end subroutine mc_init ! type (monitor_complex) function mc_mult(this,b) ! multiply monitor_complex variables type (monitor_complex), intent(in) :: this, b ! this multiplication is actually defined by function pc_mult mc_mult%pc = this%pc*b%pc ! set last operation mc_mult%last_op = 'MULTIPLY' end function mc_mult ! subroutine mc_display(this,c) ! display value of monitor_complex variable with label type (monitor_complex), intent(in) :: this character*(*), intent(in), optional :: c call display(this%pc,c) end subroutine mc_display ! subroutine last_op(this,c) ! display last operation type (monitor_complex), intent(in) :: this character*(*), intent(in) :: c print *, 'last op for ', c, ' was ', this%last_op end subroutine last_op ! type (monitor_complex) function mc(pc) ! convert private_complex object to monitor_complex object type (private_complex), intent(in) :: pc mc%pc = pc mc%last_op = 'INIT' end function mc end module monitor_complex_class Dodatek F: Končna različica razreda species_descriptor module species_descriptor_class type species_descriptor private integer :: number_of_particles real :: charge, charge_to_mass, kinetic_energy end type species_descriptor contains subroutine get_species(this,np,qm,qbm,ek) ! unpack components of species descriptor implicit none type (species_descriptor), intent(in) :: this integer, intent(out), optional :: np real, intent(out), optional :: qm, qbm, ek if (present(np)) np = this%number_of_particles if (present(qm)) qm = this%charge if (present(qbm)) qbm = this%charge_to_mass if (present(ek)) ek = this%kinetic_energy end subroutine get_species ! subroutine put_species(this,np,qm,qbm,ek) ! pack components of species descriptor implicit none type (species_descriptor), intent(out) :: this integer, intent(in), optional :: np real, intent(in), optional :: qm, qbm, ek if (present(np)) this%number_of_particles = np if (present(qm)) this%charge = qm if (present(qbm)) this%charge_to_mass = qbm if (present(ek)) this%kinetic_energy = ek end subroutine put_species end module species_descriptor_class Dodatek G: Končna različica razreda vrste module species_class ! inherit species descriptor class use species_descriptor_class type species private type (species_descriptor) :: descriptor real, dimension(:,:), pointer :: coordinates end type species interface new module procedure species_init end interface contains subroutine species_init(this,species_args,idimp,nop) ! allocate particle coordinate pointer array and store descriptor type (species), intent(inout) :: this type (species_descriptor), intent(in) :: species_args integer, intent(in) :: idimp, nop integer :: ierr ! allocate pointer array allocate(this%coordinates(idimp,nop),stat=ierr) ! check for allocation error if (ierr.ne.0) then print *,'species allocation error' ! store descriptor else this%descriptor = species_args endif end subroutine species_init ! subroutine push1(this,fx,dt) type (species), intent(inout) :: this real, dimension (:), intent(in) :: fx real, intent(in) :: dt integer :: np, idimp, nop, nx real :: qbm, ek ! extract array sizes idimp = size(this%coordinates,1) nop = size(this%coordinates,2) nx = size(fx) ! unpack input scalars from derived type with inherited procedure call get_species(this%descriptor,np=np,qbm=qbm) ! call old, ugly but fast particle pusher here call orig_push1(this%coordinates,fx,qbm,dt,ek,np,idimp, &nop,nx) ! pack output scalar into derived type with inherited procedure call put_species(this%descriptor,ek=ek) end subroutine push1 end module species_class Dodatek H: Končna različica razreda complex_subtype module complex_subtype_class ! get (inherit) private_complex type and its public procedures use private_complex_class ! get (inherit) monitor_complex type and its public procedures use monitor_complex_class private public :: private_complex, monitor_complex, complex_subtype public :: new, assign, assignment(=), operator(*), display ! define complex_subtype type type complex_subtype private type (private_complex), pointer :: pc type (monitor_complex), pointer :: mc end type complex_subtype interface assign module procedure assign_pc module procedure assign_mc end interface interface assignment (=) module procedure copy_subtype end interface interface operator(*) module procedure mult_subtype end interface interface display module procedure display_subtype end interface contains subroutine assign_pc(cs,pc) ! assign private_complex to complex_subtype type (complex_subtype), intent(out) :: cs type (private_complex), target, intent(in) :: pc cs%pc => pc nullify(cs%mc) end subroutine assign_pc ! subroutine assign_mc(cs,mc) ! assign monitor_complex to complex_subtype type (complex_subtype), intent(out) :: cs type (monitor_complex), target, intent(in) :: mc nullify(cs%pc) cs%mc => mc end subroutine assign_mc ! subroutine copy_subtype(this,b) ! assign contents of complex_subtype to complex_subtype type (complex_subtype), intent(inout) :: this type (complex_subtype), intent(in) :: b ! check if pointer is associated with private_complex type if (associated(b%pc)) then this%pc = b%pc nullify(this%mc) ! check if pointer is associated with monitor_complex type elseif (associated(b%mc)) then this%mc = b%mc nullify(this%pc) endif end subroutine copy_subtype ! function mult_subtype(this,b) result(output) ! multiply complex_subtype variables type (complex_subtype), intent(in) :: this, b type (complex_subtype) :: output type (private_complex), target, save :: tpc type (monitor_complex), target, save :: tmc ! check if pointer is associated with private_complex type if (associated(this%pc)) then tpc = this%pc*b%pc output = tpc ! check if pointer is associated with monitor_complex type elseif (associated(this%mc)) then tmc = this%mc*b%mc output = tmc endif end function mult_subtype ! subroutine display_subtype(a,c) ! display value of complex_subtype variable with label type (complex_subtype), intent(in) :: a character*(*), intent(in) :: c ! check if pointer is associated with private_complex type if (associated(a%pc)) then call display(a%pc,c) ! check if pointer is associated with monitor_complex type elseif (associated(a%mc)) then call display(a%mc,c) endif end subroutine display_subtype end module complex_subtype_class   Prevedeno iz http://www.cs.rpi.edu/~szymansk/OOF90/F90_Objects.html Domača stran
...