Upravljanje tokom
U računarstvu, upravljanje tokom predstavlja redosled po kome se naredbe, uputstva ili pozivi funkcija proveravaju ili izvršavaju. Eksplicitnim naglaskom na kontrolu toka se razlikuju imperativni programski jezici od deklarativnih programskih jezika.
U okviru imperativnog programskog jezika, naredba upravljanja tokom svojim izvršavanjem daje odgovor na pitanje kojim putem (ako postoje 2 ili više) treba nastaviti izvršavanje. Kod ne-striktnih programskih jezika, funkcijama i jezičkim konstrukcijama se dolazi do istog rezultata, ali se to ne zove nužno upravljanje tokom.
Vrste naredbi kontrole tokom koje podržavaju različiti jezici se razlikuju, ali se mogu podeliti po njihovom efektu:
- nastavak na drugoj naredbi
(bezuslovno grananje ili skok), - izvršavanje bloka naredbi samo ako je ispunjen određeni uslov (izbor, odnosno uslovna grana),
- izvršavanje sklopa naredbi nijednom ili više puta, dok se ne ispuni određeni uslov (npr. petlja - isto gao i uslovna grana),
- izvršavanje udaljenog kompleta naredbi, nakon kojeg se uglavnom tok upravljanja vraća (potprogrami, koprogrami i kontinuacije),
- zaustavljanje programa, sprečavajući dalji rad (bezuslovni zastoj).
Skup naredbi je zato generalno strukturiran kao blok, koji pored grupisanja definiše i leksički obim.
Prekidi i signali su mehanizmi niskog nivoa koji menjaju tok upravljanja na sličan način kao i potprogrami, ali se pre javljaju kao odgovor na neke eksterne stimuluse (koji mogu da se dese asinhrono), nego na „in-lajn naredbe upravljanja tokom.
Na nivou mašine ili asemblerskog jezika, instrukcije upravljanja tokom rade tako što menjaju programski registar. Za neke procesore jedina uputstva za upravljanje tokom su uslovne ili bezuslovne naredbe za grananje (skokovi).
Labela prestavlja ime ili broj eksplicitno dodeljen fiksnoj poziciji unutar izvornog koda, koji može biti pozvan naredbom upravljanja toka koja se nalazi na nekom drugom mestu unutar izvornog koda. Labela, osim što obeležava poziciju unutar izvornog koda, nema drugih funkcija.
Brojevi redova su zamena za labelu sa imenom (koji se koriste u nekim programskim jezicima kao što su Fortran i Bejsik), to su celi brojevi postavljeni na početak svakog reda teksta unutar izvornog koda. Programski jezici u kojima se ovo koristi često postavljaju uslov da broj svakog novog reda mora biti veći od broja iz prethodnog reda, ali ne mora nužno biti prvi uzastopni. Na primer, u Bejsiku:
10 LET X = 3
20 PRINT X
U drugim jezicima kao što su C i Ada, labela je identifikator koji se obično pojavljuje na početku reda, praćen kolonom. Na primer, u programskom jeziku C:
Success: printf("The operation was successful.\n");
Programski jezik Algol 60 je dozvoljavao kako cele brojeve tako i identifikatore kao labele (oba zakačena za kolonu sa određenom naredbom), ali samo nekoliko u slučaju da je neka druga varijanta Algola dozvoljavala cele brojeve.
goto naredba (reč nastala spajanjem engleskih reči gou i tu) je najjednostavnija forma bezuslovnog prenosa upravljanja.
Iako ključna reč može biti sastavljena i od velikih i od malih slova u zavisnosti od programskog jezika, obično se zapisuje kao:
goto label
Cilj goutu naredbe je da izazove izvršavanje naredbe koja je data određenom labelom (ili sledi odmah nakon nje).
Goutu naredbe se smatraju štetnim od strane mnogih stručnjaka iz oblasti računarstva, među kojima je i Edsger Dajkstra.
Terminologija reči potprogram dosta varira; alternativni nazivi su rutine, procedure, funkcije (pogotovo ako vraćaju rezultate) ili metode (posebno ako pripadaju klasama ili klasama tipa).
Tokom pedesetih godina dvadesetog veka, memorije računara su bile veoma male u poređenju sa današnjim standardima, pa su potprogrami pre svega korišćene radi smanjivanja veličine programa; deo koda je bio jednom napisan a onda korišćen mnogo puta iz različitih delova programa.
U današnje vreme, potprogrami se češće koriste sa stvaranje programa koji ima obimniju strukturu, npr. izdvajanjem određenog algoritma ili sakrivanjem određene metode prsistupa podacima, potprogrami su jedan tip modularnosti koji pomaže da se skrati posao.
U maju 1966., Bom i Jakopini su objavili članak u časopisu Komunikacije ACM-a u kojem tvrde da bilo koji program sa goutujem može da se preobrazi u program bez goutua koristeći samo izbor (IF THEN ELSE) i petlje (WHILE uslov DO xxx), moguće sa dupliranim kodom i/ili dodavanjem Bulovih varijabli (tačno/netačno). Kasnije su autori dokazali da izbor može biti zamenjen petljama (i još više Bulovih promenljivih).
Činjenica da je takav minimalizam moguć, ne znači i da je poželjan; uostalom, računarima je teoretski potrebna samo jedna mašinska instrukcija (oduzmi jedan broj od drugog i granaj ako je rezultat negativan), ali praktični računari imaju više desetina ili više stotina mašinskih instrukcija.
Bomov i Jakopinijev članak je pokazao da svi programi mogu biti bez goutu-a. Ostala istraživanja su pokazala da su kontrolne strukture sa po jednim ulazom i izlazom bile mnogo lakše za razumevanje nego bilo koja druga forma, najviše zbog toga što su mogle da se koriste svuda kao naredbe, bez remećenja upravljanja tokom. Drugim rečima, bile su kompozabilne. (Kasnija unapređenja, kao što su ne-striktni programski jezici - ili ranija, kompozabilne softverske transakcije - su pratile ovaj pravac mišljenja, što je činilo komponente programa još više kompozabilnim.)
Neki akademici su puristički pristupili rezultatima Boma i Jakopinija i započeli raspravu o tome da su čak i break
i return
naredbe koje se nalaze u petljama loše u praksi pošto uopšte nisu potrebne u navedenom dokazu, stoga su se zalagali da sve petlje treba da imaju samo jedan izlaz. Ovakav puristički pristup je osnova programskog jezika Paskal (dizajniranog 1968-1969), koji je do sredine devedesetih godina dvadesetog veka bio preferiran jezik za programere početnike na fakultetima.[1] Direktna primena Bom-Jakopini teoreme može dovesti do pojave dodatnih lokalnih promenljivih u strukturiranom grafikonu, i takođe dovesti do ponavljanja u kodu.[2] Potonji problem se naziva petlja i po u ovom kontekstu.[3] Paskal je zahvaćen sa oba navedena problema i sudeći po empirijskim istraživanjima Erika S. Robertsa, studenti su imali poteškoća sa formulacijom tačnih rešenja za nekolicinu prostih problema, uključujući pisanje funkcije koja traži element u nizu. U istraživanju Henrija Šapira iz 1980. navedenog od strane Robertsa, stoji podatak da koristeći samo kontrolne strukture koje nudi Paskal, samo 20% subjekata je dalo tačan odgovor, dok nijedan subjekat nije napisao pogrešan kod za rešenje problema ako mu je bilo dozvoljeno da napiše return naredbu u telu petlje.[1]
Većina programskih jezika koji sadrže kontrolne strukture poseduju i inicijalnu ključnu reč koja kazuje koji tip kontrolne strukture je sadržan. Jezici se tada dele na one čije kontrolne strukture imaju odnosno nemaju finalnu ključnu reč.
- Bez finalne ključne reči: Algol 60, C, C++, Haskel, Java, Paskal, Perl, PHP, PL/I, Pajton, PauerŠel. Ovakvim jezicima je potreban neki način grupisanja izjava:
- Algol 60 i Paskal :
begin
...end
- C, C++, Java, Perl, PHP i PauerŠel: vitičaste zagrade
{
...}
- PL/1:
DO
...END
- Pajton: koristi nivo uvlačenja (vidi pravilo sa-strane)
- Haskel: mogu se koristiti i metod uvlačenja i vitičaste zagrade, uz slobodu da se mešaju
- Algol 60 i Paskal :
- Finalne ključne reči: Ada, Algol 68, Modula-2, Fortran 77, Mitril, Vižual bejsik. Forme finalnih ključnih reči:
- Ada: Finalna ključna reč je
end
+ spejs + inicijalna ključna reč npr.if
...end if
,loop
...end loop
- Algol 68, Mitril: inicijalna ključna reč pisana otpozadi npr.
if
...fi
,case
...esac
- Fortran 77: finalna ključna reč je
end
+ inicijalna ključna reč npr.IF
...ENDIF
,DO
...ENDDO
- Modula-2: ista finalna ključna reč
END
za sve - Vižual bejsik: svaka kontrolna struktura ima svoju ključnu reč.
If
...End If
;For
...Next
;Do
...Loop
;While
...Wend
- Ada: Finalna ključna reč je
Uslovni izrazi i uslovne konstrukcije su odlike programskog jezika koji izvršava različite operacije u zavisnosti da li Bulov uslov vraća true ili false.
IF.
.
GOTO
. Forma koja se sreće u nestrukturiranim programskim jezicima koja imitira običnu instrukciju mašinskog koda, bi trebalo da skoči (GOTO) do oznake ili linijskog broja kada se uslov ispuni.IF.
.THEN..
(ENDIF)
. Umesto da budu ograničene na skok, bilo koja prosta naredba ili ugnežđeni bi mogli da prate THEN ključnu reč. Ovo je strukturirana forma.IF.
.THEN..ELSE..
(ENDIF)
. Kao gorenavedeno, ali preduzimajući drugu radnju ukoliko je uslov false. Ovo je jedna od najzastupljenijih formi, sa mnogo varijacija. Neke zahtevaju završnoENDIF
, neke ne. C i slični jezici ne zahtevaju završnu ključnu reč, ili 'then', ali zahtevaju zagrade oko uslova.- Uslovne naredbe su često ugnežđene unutar uslovnih naredbi. Neki jezici dozvoljavaju
ELSE
iIF
da se kombinuju uELSEIF
, izbegavajući potrebuza većim brojemENDIF
ili ostalih finalnih naredbi na kraju složene naredbe.
Manje poznate varijacije uključuju:-
- Neki jezici, kao što je Fortran, imaju "trostruko" ili "aritmetičko if", testiranje, koje proverava da li je numerička vrednost pozitivna, negativna ili nula..
- Neki jezici poseduju funkcionalnu formu "if" naredbe, na primer Lispov
cond
. - Neki jezici poseduju operatorsku formu "if" naredbe, kao na primer trojni operator u jeziku C.
- Perl pruža
if
sawhen
iunless po uzuru na C
. - Smalltalk koristi
ifTrue
iifFalse
poruke za implementaciju kondicionala, radije nego neke osnovne jezičke konstrukcije.
Naredbe zamene (ili naredbe predmeta, ili višestruke grane) porede zadatu vrednost sa određenim konstantama i započinju izvršavanje u zavisnosti od prve konstante sa kojom se poklope. Obično postoji ograničen broj uobičajenih akcija ("else", "otherwise") koje se preduzimaju ako se nijedno poklapanje ne dogodi. Naredbe zamene dozvoljavaju optimizaciju kompajlera, kao što su lukap tabele. U dinamičkim jezicima, predmeti ne moraju biti ograničeni konstantnim izrazima, i moguće je da se produže do poklapanja šablona, kao u primeru šel skripte sa desne strane, gde *)
implementira uobičajeni slučaj kao glob koji se poklapa sa bilo kojim stringom. Logika slučaja se takođe može implementirati u funkcionalnu formu, kao u SQL-ovoj decode
naredbi.
Pascal: | Ada: | C: | Shell script: | Lisp: |
---|---|---|---|---|
case someChar of
'a': actionOnA;
'x': actionOnX;
'y','z':actionOnYandZ;
else actionOnNoMatch;
end;
|
case someChar is
when 'a' => actionOnA;
when 'x' => actionOnX;
when 'y' | 'z' => actionOnYandZ;
when others => actionOnNoMatch;
end;
|
switch (someChar) {
case 'a': actionOnA; break;
case 'x': actionOnX; break;
case 'y':
case 'z': actionOnYandZ; break;
default: actionOnNoMatch;
}
|
case $someChar in
a) actionOnA ;;
x) actionOnX ;;
[yz]) actionOnYandZ ;;
*) actionOnNoMatch ;;
esac
|
(case someChar
((#\a) actionOnA)
((#\x) actionOnX)
((#\y #\z) actionOnYandZ)
(else actionOnNoMatch))
|
Petlja predstavlja niz naredbi koji se definiše jednom ali se može izvršite više puta uzastopno. Kod "unutar" petlje (telo petlje, ispod prikazano kao hhh) se izvršava određen broj puta, ili jedanput za kolekciju stavki, ili dok se ne ispuni određeni uslov, ili neograničeno mogo puta.
U funkcionalnim programskim jezicima, kao što su Haskel i Scheme, petlje se mogu iskazati pomoću rekurzije ili iteracije sa zadatom tačkom bolje nego eksplicitnim konstrukcijama petlje. Repna rekurzija predstavlja specijalni slučaj rekurzije koja se lako može pretvoriti u iteraciju.
Većina programskih jezika poseduje konstrukcije za ponavljanje petlje određen broj puta. Treba zapaziti da, ako je N manje od 1 u ovim slučajevima, onda jezik zapravo specifikuje da telo treba potpuno preskočiti, ili izvršiti jedanput ako je N=1. U većini slučajeva, brojanje je moguće i unazad, a ne mora ni biti uzastopno.
FOR I = 1 TO N | for I := 1 to N do begin xxx | xxx NEXT I | end; ------------------------------------------------------------ DO I = 1,N | for ( I=1; I<=N; ++I ) { xxx | xxx END DO | }
U velikom broju programskih jezika, samo se intidžeri mogu pouzdano koristiti za petlju koja je kontrolisana brojačem. Flouting-point brojevi nisu precizno predstavljeni zbog ograničenja hardvera, tako da petlja npr.:
for X := 0.1 step 0.1 to 1.0 do
možda može biti ponovljena 9 ili 10 puta, u zavisnosti od grešaka sa saokruživanjem brojeva i/ili hardvera i/ili verzije kompajlera. Osim toga, ako se povećanje H-a dogodi puetm ponovljenjog sabiranja, nagomilane greške u zaokruživanju značiti da se vrednost H u svakoj iteraciji može dosta razlikovati od očekivanog niza 0.1, 0.2, 0.3, ..., 1-0.
Većina programskih jezika poseduje strukture za ponavljanje petlje sve dok se neki uslov ne promeni. Treba napomenuti da je kod nekih varijacija provera na početku, a kod nekih na kraju petlje. U slučaju da je provera pre petlje, telo petlje se može u potpunosti preskočiti, ali ako se nalazi posle tela petlje, petlja se mora izvršiti bar jedanput.
DO WHILE (test) | repeat xxx | xxx LOOP | until test; ---------------------------------------------- while (test) { | do xxx | xxx } | while (test);
Prekid kontrole je metoda detekcije promene vrednosti koja se koristi u običnim petljama za započinjanje obrade grupe promenljivih. Ključna promenljiva vrednost ili vrednosti se nadgledaju unutar petlje a njihova promena menja tok rada programa u zavisnosti od zadatih naredbi koje zavise od tih promenljivih vrednosti.
DO UNTIL (End-of-File) IF new-zipcode <> current-zipcode display_tally(current-zipcode, zipcount) current-zipcode = new-zipcode zipcount = 0 ENDIF zipcount++ LOOP
Nekolicina programskih jezika (npr. Ada, D, Smalltalk, PHP, Perl, Objektni Paskal, Java, C#, Matlab, Mitril, Vižual Bejsik, Rubi, Pajton, Javaskript, Fortran 95 i kasniji) poseduju specijalne strukture koje dozvoljavaju ponavljanje petlje kroz sve elemente niza, ili sve članove kompleta ili kolekcije.
nekaKolekcija do: [:svakiElement |xxx].
for Predmet in Kolekcija do begin xxx end; foreach (predmet; mojaKolekcija) { xxx } foreach nekiNiz { xxx } foreach ($nekiNiz as $k => $v) { xxx } Kolekcija<Niska> coll; for (Niska s : coll) {} foreach (niska s in mojaNiskaKolekcija) { xxx } $nekaKolekcija | ZaSvaki-Objekat { $_ }
forall ( index = first:last:step... )
Skala ima for-naredbe, koje generalizuju petlje kontrolisane kolekcijama, i pomažu druge radnje, kao što je paralelna obrada. Haskel ima do-naredbe, koje pružaju sličnu funkcionalnost kao i for-izraza u Skali.
Strukture generalne iteracije, kao što je for-naredba u C-u i do forma u Common Lisp-u, se mogu iskoristiti za izražavanje bilo koje od gore navedenih petlji, kao i ostalih—npr. petljanje preko niza zbirki paralelno. Tamo gde se može primeniti konkretnija struktura petlje, ona je prioritetnija od obične iterativne strukture, pošto čini svrhu izraza jasnijom.
Beskonačne petlje se koriste da osiguraju da deo programa prolazi kroz petlju zauvek ili dok se ne pojavi izuzetan uslov, kao što je greška. Na primer, program koji radi na principu događaja (kao što je server) bi trebalo da se nalazi u beskonačnoj petlji, obrađujući u momentu kad se dogode , zaustavljajući se samo u slučaju kada se proces prekine od strane operatora.
Beskonačne petlje mogu biti realizovane korišćenjem drugih konstrukcija za kontrolu toka. Najčešće, kod nestrukturiranog programiranja ovo je skok unazad (goto), dok je u strukturiranom programiranju ovo beskonačna petlja (while petlja) podešena da se nikada ne završi, tako što se uslov izostavi ili jednostavno postavljanje istog uslova na True, kao while (true) .
..
. Neki jezici poseduju specijalne konstrukcije za beskonačne petlje, obično je to izostavljanje uslova iz beskonačne petlje. Neki od pomenutih jezika su Ada (loop ... end loop
),[4] Fotran (DO .
..
END DO
), Gou (for { ... }
), i Rubi (loop do ... end
).
Često se dešava da se beskonačna petlja stvori kao posledica greške u uslovno-kontrolisanoj petlji, gde petlja koristi promenljive koje se u stvari nikada ne menjaju unutar te same petlje.
Ponekad u telu petlje postoji potreba da se preskoči ostatak petlje i da se nastavi sa sledećom iteracijom petlje. U nekim jezicima postoji naredba kao što je continue
(većina jezika), skip
, ili next
(Perl i Rubi), koja će to učiniti. Cilj je da se eliminiše izvršenje tela petlje koja se nalazi u sredini koda i da se nastavi sa normalnim izvršavanjem sledeće iteracije. Ako je iteracija na poslednjem mestu u petlji, cilj je da se rano eliminiše čitava petlja.
Neki jezici, poput Perla i Rubija, poseduju redo
naredbu koja započinje trenutnu iteraciju od početka.
Rubi poseduje retry
naredbu koja pokreće celu petlju ispočetka, od prve iteracije.
Pri korišćenju petlje kontrolisane brojanjem za pretragu tabele, poželjno je da se pretraga obustavi u momentu kada se željena stavka pronađe. Neki programski jezici poseduju naredbu kao što je break
(većina jezika), exit
, ili last
(Perl), čiji je cilj da odmah prekine petlju i prebaci upravljanje na naredbu koja direktno sledi nakon petlje.
Naredni primer je prikazan u jeziku Ada koji podržava i rani izlaz iz petlje i petlje sa proverom u sredini. Obe odlike su veoma slične i samo poređenjem isečaka iz oba koda može se videti razlika: rani izlaz zahteva da stoji u kombinaciji sa if naredbom, dok je provera u sredini samostalna konstrukcija.
with Ada.Text IO;
with Ada.Integer Text IO;
procedure Štampaj_Kvadrate is
X : Integer;
begin
Read_Data : loop
Ada.Integer Text IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Štampaj_Kvadrate;
Pajton podržava uslovno izvršavanje koda u zavisnosti od toga da li je se rano izašlo iz petlje (sa break
naredbom), ili nije - koristeći se else-klauzulom sa petljom. Na primer,
for n in niz_brojeva:
if prost_broj(n):
print "Niz sadrži prost broj"
break
else:
print "Niz ne sadrži prost broj"
Treba zapaziti da je else
klauzula u gorenavedenom primeru zakačena za for
naredbu, a ne unutrašnju if
naredbu. Obe petlje u Pajtonu, for
i while
, podržavaju else klauzulu, koja se izvršava samo u slučaju da se rani izlazak iz petlje nije dogodio.
Neki jezici podržavaju prekidanje iz ugnežđenih petlji; u teorijskim krugovima, to se zove izbijanje sa više nivoa. Tipična primena ovog principa je pretraživanje višedimenzionalnih tabela. Ovo se može uraditi pomoću prekidanja sa više nivoa (prekidanje sa N nivoa), kao u bash-u[5] i PHP-u,[6] ili putem označenih prekida (izbij i nastavi kod date oznake), kao u Javi i Perlu.[7] Alternative za prekide sa više nivoa uključuju jednostruke prekide, zajedno sa neredbenim promenljivama koji se koriste za prekid još jednom nivou; izuzeci, koji se nalaze na nivou na koji se prekida; stavljanje ugnežđene petlje u funkciju i korišćenje return naredbe za izazivanje prekida čitave ugnežđene petlje; ili korišćenje oznake i goto iskaza. C ne podržava prekid sa više nivoa, i uobičajena alternativa je korišćenje goto naredbe za implementaciju označenog prekida.[8] Pajton ne poseduje prekid sa više nivoa niti nastavak – ovo je predloženo u PEP 3136, ali je odbačeno na osnovu toga što dodatna kompleksnost nije bila vredna legitimne.[9]
Pojam prekida sa više nivoa nekim delom bitna za teorijsko računarstvo, jer dovodi do nečega što se danas naziva the ,, kosaradžu hijerarhija''[10] Godine 1973. S. Rao Kosaradžu je preradio teoremu strukturiranih programa tako što je dokazao da je moguće da se izbegne dodavanje dodatnih promenljivih u strukturiranom programiranju, dok kod su proizvoljno-duboki prekidi sa više nivoa dozvoljeni u petlji.[11] Štaviše, Kosaradžu je dokazao da postoji stroga hijerarhija programa: za svaki intedžer n postoji program koji sadrži prekid sa više nivoa dubine n koji se ne može preraditi kao program sa prekidima sa više nivoa dubine manje od n bez uvođenja dodatnih promenljivih.[10]
Takođe se može koristiti return
naredba u potprogramu koja izvršava naredbe u petlji, vršeći time prekid kako ugnežđene petlje, tako i potprograma. There are other proposed control structures for multiple breaks, but these are generally implemented as exceptions instead.
U svom udžbeniku iz 2004., Dejvid Vat koristi Tenentov pojam of sekvencera za objašnjenje sličnosti prekida sa više nivoa sa return naredbama. Vat napominje da klasa sekvencera poznatih kao izlazni sekvenceri, definisanih kao "sekvencer koji prekida izvršavanje komande ili procedure koja prima tekst", obuhvata kako prekide petlji (uključujući prekide sa više nivoa) i return naredbe. Međutim, iako se često implementira, return sekvenceri takođe mogu nositi (return) vrednost, dok the break sekvencer implementiran u savremenim jezicima obično ne može.[2]
Varijante petlji i invarijante petlji se koriste za izražavanje ispravnosti petlji.[12]
U praksi, varijanta petlje je ceo izraz koji ima početnu ne-negativnu vrednost. Vrednost varijante se mora smanjitivati tokom svake iteracije petlje, ali nikad ne sme postati negativna tokom pravilnog izvršavanja petlje. Varijante petlje se koriste da zagarantuju okončanje petlje.
Invarijanta petlje je tvrdnja koja mora biti tačna pre prve iteracije u petlji i ostati tačna posle svake iteracije. Ovo podrazumeva da kada se petljanje ispravno okonča, i izlazni uslov i invarijanta petlje moraju biti zadovoljeni. Invarijante petlje se koriste za praćenje određenih svojstava petlje tokom uzastopnih iteracija.
Neki programski jezici, poput Ajfela sadrže ugrađenu podršku za varijante i invarijante petlji. U ostalim slučajevima podrška je dodatak, kao što je specifikacija Java jezika za modeliranje za naredbe u petlji u Javi.
Neki Lisp dijalekti pružaju bogat podjezik za opisivanje petlji. Rani primer se može naći u Konverzionalnom Lisp-u Interlispa. Common Lisp pruža makro za petlje koji implementira takav podjezik.[13]
Programski jezik | uslov | petlja | rani izlaz | kontinuacija | ponovni rad | ponovni pokušaj | testiranje ispravnosti | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
početni | središnji | krajnji | prebroj | kolekcija | opšta | beskonačna [1] | varijanta | invarijanta | |||||
Ada | Da | Da | Da | Da | Šablon:Partial | No | Da | duboko ugnežđeno | No | ||||
C | Da | No | Da | No [2] | No | Da | No | Šablon:Partial [3] | Šablon:Partial [3] | No | |||
C++ | Da | No | Da | No [2] | Da [9] | Da | No | Šablon:Partial [3] | Šablon:Partial [3] | No | |||
C# | Da | No | Da | No [2] | Da | Da | No | Šablon:Partial [3] | Šablon:Partial [3] | ||||
KOBOL | Da | No | Da | Da | No | Da | No | Šablon:Partial [14] | Šablon:Partial [14] | No | |||
Common Lisp | Da | Da | Da | Da | Šablon:Partial [16] | Da | Da | duboko ugnežđeno | No | ||||
D | Da | No | Da | Da | Da | Da | Da[14] | duboko ugnežđeno | duboko ugnežđeno | No | |||
Eiffel | Da | No | No | Da [10] | Da | Da | No | Šablon:Partial [10] | No | No | No [11] | Šablon:Partial [13] | Da |
F# | Da | No | No | Da | Da | No | No | No [6] | No | No | |||
FORTRAN 77 | Da | No | No | Da | No | No | No | Šablon:Partial | Da | ||||
Fortran 90 | Da | No | No | Da | No | No | Da | duboko ugnežđeno | Da | ||||
Fortran 95 and later | Da | No | No | Da | Šablon:Partial | No | Da | duboko ugnežđeno | Da | ||||
Haskell | No | No | No | No | Da | No | Da | No [6] | No | No | |||
Java | Da | No | Da | No [2] | Da | Da | No | duboko ugnežđeno | duboko ugnežđeno | No | nije ugrađen [12] | nije ugrađen [12] | |
JavaSkript | Da | No | Da | No [2] | Da | Da | No | duboko ugnežđeno | duboko ugnežđeno | No | |||
OKaml | Da | No | No | Da | nizovi, liste | No | No | No [6] | No | No | |||
PHP | Da | No | Da | No [2] [5] | Da [4] | Da | No | duboko ugnežđeno | duboko ugnežđeno | No | |||
Perl | Da | No | Da | No [2] [5] | Da | Da | No | duboko ugnežđeno | duboko ugnežđeno | Da | |||
Python | Da | No | No | No [5] | Da | No | No | Šablon:Partial [6] | Šablon:Partial [6] | No | |||
REBOL | No [7] | Da | Da | Da | Da | No [8] | Da | Šablon:Partial [6] | No | No | |||
Ruby | Da | No | Da | Da | Da | No | Da | Šablon:Partial [6] | Šablon:Partial [6] | Da | Da | ||
Standard ML | Da | No | No | No | nizovi, liste | No | No | No [6] | No | No | |||
Vižual Bejsik. NET | Da | No | Da | Da | Da | No | Da | Šablon:Partial | Šablon:Partial | ||||
Vindous PauerŠel | Da | No | Da | No [2] | Da | Da | No | Šablon:Dunno | Da |
- a
while (true)
se ne računa kao beskonačna petlja u ovu svrhu, jer nije prava jezička struktura. - a b c d e f g h C-ova
for (init; test; increment)
petlja je generalna konstrukcija petlje, ali ne nužno brojeća, iako je često korišćena u tu svrhu. - a b c Duboki prekidi se mogu ostvariti u jezicima C, C++ i C# korišćenjem oznaka i goto-a.
- a Iteracija nad objektima je dodata u PHP 5.
- a b c Petlja sa brojanjem se može imitirati iteracijom nad an listom koja se povećava ili generatorom, na primer Pajtonova
range()
naredba. - a b c d e Duboki prekidi se mogu ostvariti pomoću upravljanja izuzecima.
- a Ne postoji specijalna konstrukcija, pošto se
while
funkcija može koristiti u ovu svrhu. - a Ne postoji specijalna konstrukcija, ali korisnici mogu definisati obične funkcije petlje.
- a Nacrt C++11 je uveo for funkciju na osnovu niza. U STL postoji
std::for_each
šablon koji može vršiti iteracije nad STL kolekcijama i pozvati unarnu funkciju za svaki element.[14] Funkcionalnost se takođe može konstruisati kao macro nad ovim kolekcijama.[15] - a Petljanje kontrolisano brojanjem je pod uticajem iteracije kroz interval celih brojeva; rani izlaz iz petlje uključujući dodatni uslov za izlaz.
- a Ajfel podržava rezervisanu reč
retry
, ali se ona koristi u upravljanju izuzecima, a ne kontroli petlje. - a Zahteva Java Jezik za Modelovanje (JML).
- a Zahteva da varijante petlje budu celi brojevi; transfinit varijante nisu podržane. [1]
- a D podržava beskonačne kolekcije, i mogućnost iteracije nad tim kolekcijama. Ovo ne zahteva nikakvu specijalnu konstrukciju.
- a Duboki prekidi se mogu ostvariti korišćenjem
GO TO
i procedura. - a Common Lisp prethodi konceptu generičkog tipa kolekcija.
Mnogi programski jezici, posebno oni u kojima se praktikuje dinamičan stil programiranja, nude konstrukcije za vanlokalno upravljanje tokom. One dovode do toga da tok izvršavanja iskoči iz datog konteksta i nastavi se u nekoj određenoj tački. Uslovi, izuzeci i kontinuacije su tri opšte vrste vanlokalnih kontrolnih konstrukcija; postoje i kompleksnije vrste, kao što su generatori, koprogrami i ključna reč async.
PL/I poseduje 22 standardna uslova (npr. ZERODIVIDE SUBSCRIPTRANGE ENDFILE) koji se mogu podići i presresti putem: ON uslovnom akcijom; Programeri takođe mogu definisati i koristiti svoje imenovane uslove.
Poput nestrukturiranog if, samo jedna naredba može biti određena tako da je u mnoštvu slučajeva GOTO potreban za odluku kuda se upravljanje tokom treba nastaviti.
Nažalost, neke implementacije su imale značajan overhead kako sa prostorom tako i sa vremenom (posebno SUBSCRIPTRANGE), tako da su mnogi programeri pokušavali da ih zaobiđu korišćenjem uslova.
Prosti primeri sintakse:
ON uslov GOTO oznaka
Savremeni jezici have poseduju specijalizovane strukturirane konstrukcije za upravljanje izuzecima koje se ne oslanja na korišćenje naredbe GOTO
ili break (sa više nivoa) ili return naredbama. Na primer, u jeziku C++ može se napisati:
try {
xxx1 // Negde unutar ovoga
xxx2 // use: '''throw''' someValue;
xxx3
} catch (nekaKlasa& someId) { // uhvati vrednost od nekaKlasa
actionForSomeClass
} catch (nekiTip& anotherId) { // uhvati vrednost od nekiTip
actionForSomeType
} catch (...) { // uhvati bilo šta što još nije uhvaćeno
actionForAnythingElse
}
Bilo koji broj i varijetet catch
klauzula se može koristiti u gorenavedenom primeru. Ako ne postoji catch
koji se podudara sa određenim throw
, kontrola se vraća nazad kroz pozive potprograma i/ili ugnežđene blokove dok se ne pronađe catch
koji se poklapa ili dok se ne dostigne kraj glavnog programa, u tom slučaju se program nasilno gasi sa odgovarajućim obaveštenjem o grešci.
Zahvaljujući uticaju jezika C++, catch
je ključna reč rezervisana za deklaraciju upravljača izuzetaka koji radi na principu prepoznavanja šablona u ostalim, danas popularnim, jezicima, kao što su Java ili C#. Neki drugi jezici kao što je Ada koriste ključnu reč exception
za uvođenje upravljača izuzecima i možda upotrebe drugu ključnu reč (when
u jeziku Ada) za poklapanje šablona. Nekoliko jezika poput EplSkripta uvode plejsholdere u sintaksu upravljača izuzecima za automatsko vađenje nekoliko delova informacija kada se izuzetak dogodi. Ovaj pristup je dat u primeru ispod kosriteći on error
konstrukciju u EplSkriptu:
try
set mojBroj to mojBroj / 0
on error e number n from f to t partial result pr
if ( e = "Nemoguće deliti nulom" ) then display dialog "Ne smete to raditi"
end try
U udžbeniku Davida Vata iz 2004 godine se takođe analizira upravljanje izuzecima u okviru sekvencera (o ovome više piše u ovom članku, u odeljku o ranim izlazima iz petlji.) Vat ističe da abnormalna situacija, kao na primer aritmetičko prelivanje ili input/output kvara kao što je ,, datoteka nije pronađena“, je poput greške koja "se detektuje u nekoj jedinici programa na niskom nivou, ali [za koju] se upravljač u većini slučajeva nalazi u programskoj jedinici visokog nivoa". Na primer, program može da sadrži više poziva za čitanje datoteka, ali radnja koja treba da se izvrši u slučaju da datoteka nije pronađena zavisi od značenja (svrhe) te datoteke za program i zato se rutina za upravljanje ovom abnormalnom situacijom ne može naći u sistemskom kodu niskog nivoa. Kasnije Vat navodi da uvođenje testiranja statusnih oznaka u pozivaocu, kako bi se to zahtevalo u jednoizlaznom strukturiranom programiranju ili čak (višeizlaznom) povratnom sekvenceru, rezultuje u tome da "aplikacijski kod obično biva pretrpan testovima statusnih oznaka" i da "programer možda ne testira statusnu oznaku kao posledica lenjosti ili zaboravnosti. U stvari, abnormalne situacije predstavljene od strane statusnih oznaka su po pravilu ignorisane!" Vat naglašava da u suprotnosti sa testiranjem statusnih oznaka, izuzeci imaju suprotno opšte ponašanje, što dovodi do gašenja programa osim u slučaju da se programer ne pozabavi sa izuzetkom na neki način, npr. dodajući u kod naredbe za ignorisanje. Na osnovu ovih argumenata, Vat donosi zaključak da jump sekvenceri ili escape sekvenceri nisu pogodni koliko i sekvencer izuzetaka sa dolenavedenom semantikom.[2]
U jezicima Obdžekt Paskal, D, Java, C#, i Pajton, klauzula finally
se može dodati na try
konstrukciju. Bez obzira na to kako kontrola napušta try
, kod unutar finally
klauzule se zagarantovano izvršava. Ovo je korisno u slučaju pisanja koda koji treba da se odrekne bitnih resursa (kao što je otvorena datoteka ili veza sa bazom podataka) na kraju izračunavanja:
FileStream stm = null; // C# primer
try {
stm = new FileStream ("logfile.txt", FileMode.Create);
return ProcessStuff(stm); // možda izbaci izuzetak
} finally {
if (stm != null)
stm.Close();
}
Pošto je ovakav šablon dosta zastupljen, C# ima specijalnu sintaksu:
using (FileStream stm = new FileStream ("logfile.txt", FileMode.Create)) {
return ProcessStuff(stm); // možda izbaci izuzetak
}
Po napuštanju using
-bloka, kompilator garantuje da je stm
objekat oslobođen, spajajući tako promenljivu za tok datoteka dok paralelno abstrahuje od sporednih efekata inicijalizacije i oslobađanja datoteke. Pajtonova with
naredba i Rubijev blok argument za File.open
se koriste uz slične ishode.
Svi gorepomenuti jezici definišu standardne izuzetke i okolnosti pod kojima su izbačeni.
Korisnici mogu da izbacuju sopstvene izuzetke; C++ dozvoljava korisnicima da izbace i hvataju bilo koji tip, uključujući osnovne tipove poput int
, dok neki drugi jezici ne dozvoljavaju takve radnje.
C# 5.0 je uveo async ključnu reč kao podršku asinhronom I/O u "direktnom stilu".
Generatori, takođe poznati pod nazivom polu-koprograme, dozvoljavaju privremenu predaju upravljanja potrošačkoj metodi, obično koristeći ključnu reč yield
. Poput async ključne reči, ovo podržava programiranje u "direktnom stilu".
Koprogrami su funkcije koje mogu da međusobno predaju kontrolu - oblik ko-operativnog multitaskinga.
Koprogrami se mogu implementirati kao biblioteka u slučaju da programski jezik omogućava kontinuacije ili generatore - tako da razlike između koprograma i generatora u praksi postaju samo tehnički detalj.
Programski jezik | uslovi | izuzeci | generatori/koprogrami | async |
---|---|---|---|---|
Ada | No | Da | ? | ? |
C | No | No | No | No |
C++ | No | Da | da, pomoću BOOST-a | ? |
C# | No | Da | Da | Da |
Cobol | Da | Da | No | No |
Common Lisp | Da | No | ? | ? |
D | No | Da | Da | ? |
Eiffel | No | Da | ? | ? |
Erlang | No | Da | Da | ? |
F# | No | Da | Da | Da |
Go | No | Da | Da | ? |
Haskell | No | Da | Da | No |
Java | No | Da | No | No |
Objektni C | No | Da | No | ? |
PHP | No | Da | Da | ? |
PL/I | Da | No | No | No |
Python | No | Da | Da | ? |
REBOL | Da | Da | No | ? |
Ruby | No | Da | Da | ? |
Scala | No | Da | No | pomoću eksperimentalne ekstenzije |
Tcl | pomoću tragova | Da | Da | pomoću petlje događaja |
Vižual Bejsik . NET | Da | Da | No | ? |
Vindous PauerŠel | No | Da | No | ? |
U satiričnom članku[16] lista Datamejšn iz 1973., R. Lorens Klark je predložio da se GOTO naredba može zameniti COMEFROM naredbom, i naveo neke zanimljive primere. Ovo je zaista implementrirano u jeziku INTERCAL, a namenski ezoteričnom programskom jeziku.
U svom članku iz 1974. "Strukturirano Programiranje sa go to Naredbama",[17] Donald Knut je prepoznao dve situacije koje nisu objašnjene gorenavedenim kontrolnim strukturama, i izneo primere kontrolnih struktura koje bi mogle da upravljaju ovim situacijama. Uprkos njihovoj korisnosti, ove konstrukcije i dalje nisu zastupljene u konvencionalnim programskim jezicima.
Predloženo od strane in 1972:[18]
loop loop xxx1 read(char); while test; while not atEndOfFile; xxx2 write(char); repeat; repeat;
Ako se izostavi xxx1, dobijamo petlju sa testiranjem na vrhu.
Ako se izostavi xxx2, dobijamo petlju sa testiranjem na kraju.
Ako se while izostavi, dobijamo beskonačnu petlju.
Zato ova jedna konstrukcija može zameniti više različitih konstrukcija u većini programskih jezika.
Jedna od mogućih varijanti je da se dozvoli testiranje while naredbe više puta; unutar petlje, ali upotreba exitwhen (pogledaj sledeći odeljak) bolje pokriva ovaj slučaj.
Jezici koji ne poseduju ovu konstrukciju mogu da je imitiraju koristeći idiome beskonačnih petlji sa izlazom:
while (true) { xxx1 if (not test) break xxx2 }
U jeziku Ada, gorenavedena konstrukcija petlje (loop-while-repeat) se može predstaviti korišćenjem standardne beskonačne petlje (loop - end loop) koja ima exit when klauzulu u sredini (ne treba pomešati sa exitwhen naredbom iz narednog odeljka).
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Print_Squares is
X : Integer;
begin
Read_Data : loop
Ada.Integer_Text_IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Print_Squares;
Dodavanje imena petlji (kao Read_Data u ovom primeru) je opcionalno ali dozvoljava izlaženje iz spoljne petlje koja se nalazi u nekoliko ugnežđenih petlji.
Ovo je predložio Can1974.[19] Promenjena verzija je prikazana ovde.
exitwhen DogađajA or DogađajB or DogađajC; xxx exits DogađajA: radnjaA DogađajB: radnjaB DogađajC: radnjaC endexit;
exitwhen se koristi za specifikaciju događaja koji se mogu odigrati unutar xxx, njihovo događanje je dokazano korišćenjem imena događaja kao naredbe.
Kad se neki događaj manifestuje, izvrši se relevantna radnja, i upravljanje se prebacuje odmah nakon endexit.
Ova konstrukcija pruža veoma jasnu razliku između određivanja da li se neka situacija primenjuje i radnje koja se treba izvržiti za tu situaciju.
exitwhen je po konceptu slično upravljanju izuzetaka, a u ovu svrhu se koriste izuzeci ili slične konstrukcije u dosta jezika.
Prost primer koji sledi uključuje pretragu dvodimenzionalne tabele u cilju pronalaženja određene stavke.
exitwhen found or missing; for I := 1 to N do for J := 1 to M do if table[I,J] = target then found; missing; exits found: print ("item is in table"); missing: print ("item is not in table"); endexit;
- Grana (računarstvo)
- dijagram upravljanja tokom
- Grafik upravljanja tokom
- Tabela upravljanja
- Koprogram
- Ciklomatička kompleksnost
- Dijagram toka
- GOTO
- Džeru, pomaže pri učenju kontrolnih struktura
- Glavna petlja
- Rekurzija
- Raspoređivanje
- Špageti kod
- Strukturirano programiranje
- Potprogram
- Naredba zamene, uslovno menja tok upravljanja
- ↑ 1,0 1,1 Roberts, E. [1995] “Loop Exits and Structured Programming: Reopening the Debate Arhivirano 2014-07-25 na Wayback Machine-u,” ACM SIGCSE Bulletin, (27)1: 268–272.
- ↑ 2,0 2,1 2,2 David Anthony Watt; William Findlay (2004).
- ↑ Kenneth C. Louden; Kenneth A. Lambert (2011).
- ↑ Ada Programming: Control: Endless Loop
- ↑ Advanced Bash Scripting Guide: 11.3.
- ↑ PHP Manual: "break"
- ↑ perldoc: last
- ↑ comp.lang.c FAQ list · "Question 20.20b"
- ↑ [Python-3000] Announcing PEP 3136, Guido van Rossum
- ↑ 10,0 10,1 Kozen, Dexter (2008).
- ↑ KOSARAJU, S. RAO.
- ↑ Meyer 1991
- ↑ "Common Lisp LOOP macro".
- ↑ for_each. Sgi.com. Retrieved on 2010-11-09.
- ↑ Chapter 1. Boost.Foreach Arhivirano 2010-01-29 na Wayback Machine-u. Boost-sandbox.sourceforge.net (2009-12-19). Retrieved on 2010-11-09.
- ↑ „We don't know where to GOTO if we don't know where we've COME FROM.”. Arhivirano iz originala na datum 2018-07-16. Pristupljeno 2019-01-17.
- ↑ Knuth, Donald E. "Structured Programming with go to Statements" ACM Computing Surveys 6(4):261-301, December 1974.
- ↑ Dahl & Dijkstra & Hoare, "Structured Programming" Academic Press, 1972.
- ↑ Zahn, C. T. "A control statement for natural top-down structured programming" presented at Symposium on Programming Languages, Paris, 1974.
- Go To Statement Considered Harmful
- A Linguistic Contribution of GOTO-less Programming Arhivirano 2018-07-16 na Wayback Machine-u
- Structured Programming with Go To StatementsPDF (2.88 MB) PDF (2.88 MB)
- IBM 704 ManualPDF (31.4 MB) PDF (31.4 MB)