U programiranju, potprogram je niz programskih instrukcija koje obavljaju određeni zadatak, upakovane kao jedinica. Ova jedinica se zatim može koristiti u programima kad god treba izvesti taj konkretan zadatak. Potprogrami se mogu definisati u okviru programa, ili odvojeno u bibliotekama, koje se mogu iskoristiti u raznovrsnim programima. U različitim programskim jezicima, potprogram se može nazvati: procedura, funkcija, rutina, metod ili potprogram. Generički termin pozivna jedinica se ponekad koristi.[1]

Kao što samo ime potprograma sugeriše, potprogram se ponaša na isti način kao i računarski program koji se koristi kao jedan korak šireg programa ili drugog potprograma. Potprogram je često kodiran tako da može startovati (pozvati) nekoliko puta i sa više mesta u toku jednog izvršenja programa, i iz drugih potprograma, a zatim se prespoji nazad (povratak) na sledeće instrukcije nakon poziva čim je zadatak potprograma završen. Moris Vilkis, Dejvid Viler, a Stenli Gil su zaduženi za pronalazak ovog koncepta, koji su nazvali zatvoreni potprogram,[2][3] nasuprot otvorenom potprogramu ili makrou.[4]

Potprogrami su moćan programski alat,[5] i sintaksa mnogih programskih jezika uključuje podršku za pisanje i korišćenje. Razumno korišćenje potprograma (na primer, kroz strukturirano programiranje pristupa) često značajno smanji troškove razvoja i održavanja velikog programa, dok je povećan kvalitet i pouzdanost.[6] Potprogrami, često prikupljeni u bibliotekama, predstavljaju važan mehanizam za deljenje i trgovanje softvera. Disciplina objektno-orijentisano programiranje je bazirana na objektima i metodama (koji su potprogramima vezani za ove objekte ili klase objekata).

U postupku kombinovanja metod nazvan tretirani kod, izvršni program je u osnovi niz podrutinskih poziva.

Osnovni pojmovi uredi

Sadržaj jednog potprograma je njegovo telo, koje je deo programskog koda koji se izvršava kada se potprogram zove ili priziva.

Potprogram može biti napisan tako da očekuje da će dobiti jednu ili više vrednosti podataka iz pozivajućeg programa (njegove parametre ili formalnih parametara). Raspisivanje program pruža stvarne vrednosti za ove parametrnj, koji se zovu argumenti. Različiti programski jezici mogu koristiti različite konvencije za donošenje argumenata

Konvencija Opis Zajednička upotreba
Poziv po vrednosti Argument je ocenjen i kopija vrednosti je prosleđena

potprogramu

Uobičajeno većina Algol jezika posle Algol 60, poput Paskala, Delfija, Simula, CPL, PL/M, Modula, Oberon, Ada, i mnogih drugih.
Poziv po referenci Pozivanje na argumente, obično se prenosi njegova adresa Izbor kod većine Algol jezika posle Algol 60, poput Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, i mnogih drugih. C (strukture i nizovi), C++, Fortran, PL/I
Poziv po rezultatu Parametar vrednost je kopiran nazad u argument na povratku iz potprograma Ada OUT parametri
Poziv po vrednosti-rezultatu Parametar vrednost je kopiran nazad na ulazu u potprogram i opet u povratku Algol
Poziv po imenu Kao makro - menja parametre sa bezvrednim argumentima izraza Algol, Scala
Poziv po konstantnoj vrednosti Kao poziv po vrednosti samo što se taj parametar posmatra kao konstanta PL/I NONASSIGNABLE parametri, Ada IN parametri

Podrutina može vratiti izračunate vrednosti na svojoj koler (njegove povratne vrednosti), ili da obezbedi razne rezultate vrednosti ili izlaznie parametre. Zaista, uobičajena upotreba potprograma je da sprovede matematičke funkcije, u kojima je svrha potprograma samo da izračuna jedan ili više rezultata čije vrednosti se u potpunosti određuju parametrima u potprograma. (Primeri mogu uključiti izračunavanju logaritam broja ili determinante matrice.)

Potprogram poziva može imati neželjene efekte kao što su menjanje strukture podataka u memoriji računara, čitajući ili pišući na periferne uređaje, kreiranje datoteke, zaustavljanje programa ili mašine, ili čak odlaganje izvršenja programa za određeno vreme. Potprogram sa sporednim efektima može da vrati različite rezultate svaki put kada se zove, čak i ako to se zove sa istim argumentima. Primer je slučajna funkcija broj, dostupan na mnogim jezicima, da vrati različite pseudo slučajne brojeve svaki put kada se zove. Široka upotreba potprograma sa neželjenim efektima je karakteristična za imperativne programske jezike.

Potprogram može biti kodiran tako da može da se pozove rekursivno, na jednom ili više mesta, da izvrši svoj zadatak. Ovaj metod omogućava direktnu primenu funkcija definisanih matematičkim indukcijama i rekurzivnim podeli pa vladaj algoritmima.

Potprogram čija je svrha da se izračuna jedna vrednost funkcije (to jest da odgovorite na pitanje da/ne) se ponekad naziva predikat. U logici programskih jezika, često sve potprograme zovu predikatima, jer su pre svega određuju uspeh ili neuspeh. Na primer, bilo koji tip funkcije je rutina, ali ne i glavna.

Jezička podrška uredi

Programski jezici visokog nivoa obično uključuju specifične konstrukcije:

  • ograničavanje deo programa (tela) koji sačinjava potprogram
  • dodeljivanje identifikatora (ime) potprogramu
  • navođenje imena i tipova podataka parametara i vrednosti za povratak
  • lično imenovanje prostora za privremene promenljive
  • identifikaciju promenljive izvan potprograma koji su dostupni u njemu
  • pozivanje potprograma
  • dodeljivanje vrednosti parametrima
  • navođenje povratnih vrednosti iz svog tela
  • povratan na pozivajući program
  • raspolaganje vrednostima vraćenim pozivom
  • rešavanje svih izuzetaka sa kojima se sreću tokom poziva
  • pakovanje potprograma u module, biblioteke, objekte, klase, itd.

Neki programski jezici, kao što su Pascal, Fortran, Ada i mnogi dijalekti Basic, razlikuju funkcije ili funkcije potprograma, koji pružaju izričitu povratnu vrednost u pozivajući program, i potprograma ili procedure, koje ne. U tim jezicima, funkcije pozivi su obično ugrađeni u izrazima (na primer sqrt može biti pozvana sa y = z + sqrt(x)). Funkcija se ponaša isto sintaksički kao izjava (na primer print procedura može biti pozvana sa if x > 0 then print(x) ili se eksplicitno poziva kao CALL ili GOSUB (na primer call print(x) Drugi jezici kao što su C i Lisp, ne prave razliku između funkcija i potprograma.

U strogo funkcionalnim programskim jezicima kao što su Haskell, potprogram može imati nikakve neželjenih efekata, što znači da se različiti interni delovi programa neće promeniti. Funkcije će uvek vratiti isti rezultat ako više puta ovete sa istim argumentima. Takvi jezici obično podržavaju samo funkcije, jer potprogrami koje ne vraćaju vrednost nemaju koristi, osim ako oni mogu izazvati sporedne efekte.

U programskim jezicima kao što su S, S++ i S #, Potprogrami takođe mogu jednostavno da se nazivaju funkcije, ne treba mešati sa matematičkim funkcijama ili funkcionalnim programiranjem, koji su različiti koncepti.

Prevodilac jezika će obično prevesti proceduru poziva i povratak u uputstva mašine u skladu sa dobro definisanom pozivnom konvencijom (kongresom), tako se da potprogrami mogu prevesti odvojeno od programa koji ih pozivaju. Nastavne sekvence koje odgovaraju pozivu i povratnim izjavama nazivaju se postupni uvod i epilog.

Prednosti uredi

Prednosti razdvajanja programa u potprograme uključuju:

  • Razbijanje kompleksnog zadatka programiranja u jednostavnije korake: ovo je jedan od dva glavna alata strukturnog programiranja, zajedno sa strukturama podataka
  •  Smanjenje duplikata koda u okviru programa
  • Omogućava ponovnu upotrebu koda preko više programa
  • Podela velikog programerskog zadatka različitim programerima, ili različitim fazama projekta
  • Skrivanje detalja implementacije od korisnika potprograma
  • Poboljšanje sledljivosti (to jest većina jezika nudi načine da se dođe do traga poziva koji uključuje imena uključenih potprograma, a možda čak i do više informacija poput imena datoteka i brojeva linija); bez razlaganja koda u potprograme, otklanjanje grešaka bilo bi nemoguće.

Nedostaci uredi

Pozivajući se na potprogram (nasuprot korišćenju in-line koda) nameće neka računarska preopterećenja u pozivnom mehanizmu.

Potprogram obično zahteva standardan administrativni kod - kako na ulazu tako i na izlazu funkcije (funkcija uvoga i epiloga - obično štedi opšte registre svrhe i vraća adresu kao minimum).

Istorija uredi

Ideja o potprograma je počela da funkcioniše nakon što su računarske mašine postojale već neko vreme. Aritmetičke i uslovne instrukcije rasta planirane su unapred i da promenile su se relativno malo; ali posebna uputstva se koriste za procedurne pozive su se veoma mnogo promenila tokom godina. Najraniji računari i mikroprocesori, kao što je Eksperimentalna mašina manjeg obima i RCA 1802( Small-Scale Experimental Machine i RCA 1802,), nije imala nijednu instrukciju potprogramskog poziva. Potprogrami su mogli biti realizovani, ali je potrebno da programeri koriste pozivne sekvence - niz instrukcija - na svakoj lokaciji poziva. Neki vrlo stari računari i mikroprocesori, kao što su IBM 1620, Intel 8008, i PIC, imaju jedanu instrukciju potprogramskog poziva koji koristi namenski hardversko skladište za čuvanje povratne adrese - ovakav hardver podržava samo nekoliko nivoa ugnježdenih potprograma, ali može da podrži rekurzivne potprograme. Mašine pre sredine 1960-ih - kao što je UNIVAC I, PDP-1, i IBM 1130 - obično koriste pozivnu konvenciju koja je sačuvala brojač sa uputstvom u prvoj memorijskoj lokaciji pod nazivom potprograma. Ovo omogućava proizvoljno duboke nivoe ugnjećdene potprograme, ali ne podržava rekurzivne potprograme. PDP-11 (1970) je jedan od prvih računara sa potprogramskom pozivnom instrucijom sa skladištenjem; Ova funkcija podržava proizvoljno duboko ugnježdene potprograme i podržava rekurzivne potprograme.

Jezička podrška uredi

U ranim montažama, podrška potprograma bila je ograničena. Potprogrami nisu eksplicitno odvojeni jedni od drugih ili od glavnog programa, i zaista izvorni kod jednog potprograma može se smenjivati sa onim drugim kodom potprograma. Neki monteri će ponuditi predefinisane makroe za generisanje poziva i vraćanje sekvence. Do 1960-ih, monteri su obično imali mnogo sofisticiraniju podršku i za spojene i odvojene okupljene potprograme koji bi mogli biti povezani zajedno.

Potprogramske biblioteke uredi

Čak i sa ovim nezgrapnim pristupom, potprogrami su se pokazali veoma korisnim. Zbog jedne stvari su dozvolili isti kod koji se koristi u mnogim različitim programima. Memorija je bila vrlo oskudan resurs na starim računarima, a Potprogrami dozvoljavaju značajne uštede u veličini programa.

U mnogim starim računarima, uputstva programa su ušla u memoriju od papirne trake. Svaki potprogram je tada mogao biti obezbeđen posebnim komadom trake, utovaren ili spojen pre ili posle glavnog programa; a ista potprogram traka bi tada mogla da se koristi od strane mnogih različitih programa.[7] Sličan pristup je korišćen u računarima čiji je glavni ulaz kroz bušene kartice. Ime potprogram biblioteka je prvobitno značilo biblioteku, u doslovnom smislu, koja čuva indeksirane kolekcije takvih traka ili kartica za kolektivno korišćenje.

Povratak indirektnim skokom uredi

Da biste uklonili potrebu za samo-modifikaciju koda, računarski dizajneri su najzad obezbedili posrednu skok instrukciju, čiji operand je, umesto da bude povratna adresa, bio lokacija na promenljive ili procesor registar koji sadrži povratnu adresu.

Na tim računarima, umesto menjanja povratnog skoka potprograma, pozivni program bi sačuvao povratne adrese u promenljivoj, tako da kada se potprogram završi, izvrši indirektan skok - neposredno izvršenje na lokaciji koju je dao unapred definisanom promenljivom.

Skok na potprogram uredi

Drugi napredak je bio skok u potprogramski instrukciju, koji je kombinovao uštedu povratne adrese sa pozivnim skoka, čime se značajno minimizira preopterećenje.

U IBM System/ 360, na primer, uputstva filijale BAL ili BALR, namenjena za pozivni postupak, bi sačuvali povratne adrese u registru procesora navedenog u uputstvu. Da biste se vratili, potprogram je samo trebalo da izvrši indirektno prespajanje instrukciju preko tog registra. Ako potprogram treba registrar za neku drugu svrhu (kao što je pozivanje još jednog potprograma), to bi sačuvalo sadržaj registra na privatnu memorijsku lokacije ili registarsko skladište.

U sistemima kao što su HP 2100 je JSB instrukcija bi izvršila sličan zadatak, osim što je povratna adresa čuva u memorijskoj lokaciji koja je bila meta grane. Izvršenje postupka bi zapravo počelo na narednoj memorijskoj lokaciji. U HP 2100 montažnom jeziku pisaće na primer

       ...
       JSB MYSUB    (Позива потпрограм MYSUB.)
 BB    ...          (Враћа овде када је MYSUB завршен.)

da pozove potprogram nazvan MYSUB iz glavnog programa. Potprogram će biti kodirani kao

 MYSUB NOP          (Складиште ѕа повратне адресе MYSUB.)
 AA    ...          (Почетак на телу MYSUB.)
       ...
       JMP MYSUB,I  (Враћа се на позив програма.)

JSB instrukcija je postavila adresu sledeće instrukcije (imenovana kao BB) u lokaciji koja je navedena kao svoj operand (imenovan kao MYSUB), a zatim je razgranala na sledeću lokaciju nakon toga (tj AA = MYSUB + 1). Potprogram je onda mogao da se vrati u glavni program izvršavanjem indirektnog skoka JMP MYSUB, koji se razgranao do lokacije čuvane na lokaciji MYSUB.

Kompajleri za Fortran i druge jezike su mogli lako da iskoriste ova uputstva kad su na raspolaganju. Ovaj pristup podržao je više nivoa poziva; Međutim, pošto su povratna adresa, parametri i povratne vrednosti potprograma dodeljeni fiksnoj memorijskoj lokaciji, to nije dozvoljavalo rekurzivne pozive.

Inače, sličan metod je korišćen od strane Lotus 1-2-3, u ranim 1980-ih, da se otkrije preračunate zavisnosti u tabeli. Naime, lokacija je bila rezervisana u svakoj ćeliji da sačuva povratnu adresu. Pošto kružne reference nisu dozvoljen za prirodni preračun, to omogućava slobodnu šetnju bez rezervisanja prostora za skladište u memoriji, koja je veoma ograničeno na malim računarima kao što je IBM PC.

Skladište poziva uredi

Većina modernih implementacija koristi Call stack, poseban slučaj strukture skladišta podataka, da sprovede potprogramski poziva i povratak. Svaki postepeni poziv kreira novu stavku koja se zove stek(skladišni) okvir, na vrhu gomile; kada se postupak vrati, njen stek okvir se briše iz gomile, a njen prostor može se koristiti i za druge postupne pozive. Svaki stek okvir sadrži privatne podatke odgovarajućeg poziva, što obično uključuje postupne parametre i unutrašnje varijable, kao i povratnu adresu.

Pozivna sekvenca može da se realizuje nizom običnih uputstava (pristup koji se još uvek koristi u smanjenom setu instrukcija računarstva (RISC) i veoma dugim instrukcijama vorda (VLIW) arhitekture), ali mnoge tradicionalne mašine dizajnirane od kraja 1960-ih su uključili posebna uputstva za tu svrhu.[8][9][10]

Skladišni poziv se obično sprovodi kao susedno područje memorije. To je proizvoljno dizajniran izbor, bilo da je dno skladišta najniža ili najviša adresa u ovoj oblasti, tako da skladište može da unapred ili unazad raste u memoriji; Međutim, mnoge arhitekture su izabrale ovo drugo.

Neki projekti, naročito neke četvrte implementacije, koriste dve odvojene gomile, jedna uglavnom za upravljačke informacije (kao što su povratak adrese i petlje brojače), a drugi za podatke. Bivši je, ili je radio kao, skladišni poziv i da je samo indirektno dostupan za programera putem drugih jezičkih konstrukcija, dok je drugi bio više direktno dostupan.

Kada su prvi put uvedeni procedurni pozivi bazirani na skladištu, važan motiv je bio da se spase dragocena memoriju. U ovoj šemi, kompajler ne mora da rezerviše poseban prostor u memoriji za privatne podatake (parametare, povratnu adresu, i lokalne varijable) svakog postupka. U svakom trenutku, stek sadrži samo privatne podatke o pozivima koji su trenutno aktivni (naime, koji su pozvani, ali se još uvek nisu vratili). Zbog načina na koji su programi obično sastavljeni od biblioteka, bilo je (i još uvek je) neobično da se pronađu programe koji uključuju hiljade potprograma, od kojih je samo nekolicina aktivna u svakom trenutku. Za takve programe, pozivni stek mehanizam je mogao spasiti značajne količine memorije. Zaista, pozivni stek mehanizam se može smatrati kao najraniji i najjednostavniji način za automatsko upravljanje memorijom.

Međutim, još jedna prednost metode ckladišnog poziva je što omogućava rekurzivne potprogramske pozive, jer svaka ungežden poziv istog postupka dobija posebano skladište svojih privatnih podataka.

Odloženo skladištenje uredi

Jedan od nedostataka u Call stack mehanizma su povećani troškovi postupnog poziva i njegovog povratka. Dodatni troškovi uključuju da se uvećava i umanjuje stek pokazivač (i, u nekim arhitekturama, proverava pregomilavanje skladišta), i pristupanje lokalnim promenljivim i parametrima relativnih adresa, umesto apsolutnih adresa. Troškovi se mogu realizovati povećanjem vremena izvršenja, odnosno povećanjem složenost procesora, ili oboje.

Ovo iznad je najočiglednije i nepoželjano u postupcima lista ili funkcijama lista, koji se vraćaju bez ikakvih procedurnih poziva

Како би смањили оптерећеност, многи модерни компајлери покушавају да одложе примену стек позива док то није заиста потребно. 
На пример, поступни позив P може складиштити повратне адресе и параметре поступних позива у појединим процесорима регистара, и пренети контролу до поступног тела простим скоком. Ако се поступак P врати без било ког другог позива, стек се не користи уопште. Ако P треба да позове другу процедуру Q, онда ће користити складишни позив да сачува садржај било ког регистара (као што је повратна адреса) који ће бити потребан након повратка Q.

C i C++ primeri uredi

U C i C++programskim jezicima, potprogrami se nazivaju funkcije (ili funkcija članica kada je povezan za klase). Ovi jezici koristite posebne ključne reči da ukazuju na to da funkcija ne uzima nikakve parametre (naročito u C ) ili ne vraćaju nikakvu vrednost. Imajte na umu da je C i C++ funkcije mogu imati neželjene efekte, uključujući menjanje bilo koje promenljive čije adrese su prošle kao parametri (tj odnosno kao reference). Primeri:

 void function1(void) { /* some code */ }

Funkcija ne vraća vrednost i mora da bude pozvana kao samostalna funkcija, npr., function1();

 int function2(void)
 {
     return 5;
 }

Ova funkcija vraća rezultat (broj 5), a poziv može biti deo izraza, npr., x + function2()

 char function3(int number)
 {
     char selection[] = {'S','M','T','W','T','F','S'};
     return selection[number];
 }

Ova funkcija konvertuje broj između 0 do 6 u početnom slovu odgovarajućeg dana u nedelji, odnosno 0 do 'S', 1 na 'M', ..., 6 na 'S'. Rezultat pozivanja može se dodeliti promenljivoj, na primer, num_day = function3(number);.

 void function4(int *pointer_to_var)
 {
     (*pointer_to_var)++;
 }

Ova funkcija ne vraća vrednost, ali menja promenljivu čija adresa je prošla kao parametar; bila bi pozvana kao "function4(&variable_to_increment);".

Visual Basic 6 examples uredi

U Visual Basic 6 jeziku potptogrami se nazivaju funkcije. Visual Basic 6 koristi različite termine nazvane tipovima koji definišu šta je sve dato kao parametar. Po pravilu jedna neodređena promenljiva je registrovana kao tip varijante i može biti usvojena kao ByRef ili ByVal. Takođe, kada se proglašava funkcija, navodi se javna, privatna ili prijatelj oznaka, koja određuje da li se može pristupiti izvan modula ili projekta.

  • Po vrednosti [ByVal] – način donošenja vrednosti argumenta u postupku, umesto donošenja adrese. Ovo omogućava postupak pristupa kopiji promenljive. Kao rezultat toga, stvarna vrednost jedne promenljive ne može se menjati postupkom koji je donet.
  •   Po referenci [ByRef] – način donošenja adrese argumenta za procedure, umesto donošenja vrednosti. Ovo omogućava postupak pristupa stvarnoj promenljivoj. Kao rezultat toga, stvarna vrednost jedne promenljive može se zameniti postupkom kroz koji je prošla. Ukoliko nije drugačije naznačeno, argumenti su prošli po referenci.
  • Javni (opcioni) - ukazuje na to da postupak funkcija je dostupan svim drugim postupcima u svim modulima. Ako se koristi u modulu koji sadrži opciju privatno, postupak nije dostupan van projekta.
  • Privatno (opcioni) - ukazuje na to da postupak funkcija je dostupan samo drugim postupcima u modulu gde je deklarisan.
  • Prijatelj (opcioni) - koristi se samo u klasi modula. Ukazuje na to da je postupak funkcija vidljiv tokom celog projekta, ali nije vidljivo za kontrolora instance objekta.
    Private Function Function1()
        ' Some Code Here
    End Function
    
    Funkcija  ne vraća vrednost i mora biti pozvana kao samostalna funkcija, na primer, Function1
    Private Function Function2() as Integer
        Function2 = 5
    End Function
    
    Ova funkcija vraća rezultat (broj 5), a poziv može biti deo izraza, na primer, x + Function2()
    Private Function Function3(ByVal intValue as Integer) as String
        Dim strArray(6) as String
        strArray = Array("M", "T", "W", "T", "F", "S", "S")
        Function3 = strArray(intValue)
    End Function
    
    Ova funkcija konvertuje broj između 0 do 6 u početnom slovu odgovarajućeg dana u nedelji, odnosno 0 do 'M', 1 na 'M', ..., 6 na 'S'. Rezultat pozivanja može se dodeliti promenljivoj, na primer, num_day = Function3(number).
    Private Function Function4(ByRef intValue as Integer)
        intValue = intValue + 1
    End Function
    
    Ova funkcija ne vraća vrednost, ali menja promenljivu čija adresa je prošla kao parametar; bila bi pozvana kao "Function4(variable_to_increment)".

PL/I primer uredi

PL/I sam postupak poziva može biti usvojen na deskriptor koji pruža informacije o argumentu, kao što su dužina žica i granice. Ovo omogućava da postupak bude opšti i eliminiše potrebu da programer prođe takve informacije. Po difoltu PL/I pušta argumente kao reference. A (trivijalno) potprogram da promenite znak svakog elementa dvodimenzionalnog niza može da izgleda ovako:

  change_sign: procedure(array);
    declare array(*,*) float;
    array = -array;
    end change_sign;

To bi se moglo pozvati različitim nizovima kao što sledi:

  /* first array bounds from -5 to +10 and 3 to 9 */
  declare array1 (-5:10, 3:9)float;
  /* second array bounds from 1 to 16 and 1 to 16 */
  declare array2 (16,16) float;
  call change_sign(array1);
  call change_sign(array2);

Lokalne promenljive, rekurzija i ponovni ulazi uredi

Potprogram može smatrati korisnim da iskoristi određene količine ogrebotina prostora; to jest, memorija se koristi prilikom izvršenja tog potprograma da održi međurezultate. Promenljive koje se čuvaju u ovom prostoru grebanja se nazivaju lokalne promenljive, a prostor grebanje se naziva rekord za aktiviranje. Rekord aktivacija obično ima povratnu adresu koja govori gde da prođe kontrolu kada se potprogram završi.

Potprogram može imati bilo koji broj i prirodu poziva sajtovima. Ako je podržana rekurzija, potprogram možda čak i sebe da zovnj, izazivajući obustavu njegovog izvršenja dok se drugi potprogra javlja. Rekurzija je korisno sredstvo za pojednostavljivanje nekih složenih algoritama, i razbija složene probleme. Rekurzivni jezici generalno daju novu kopiju lokalnih promenljivih na svaki poziv. Ako programer želi vrednost lokalnih promenljivih da ostanu iste između poziva, mogu se proglasiti statičnim u nekim jezicima, odnosno globalne vrednosti ili zajedničke prostorije mogu da se koriste. Ovde je primer rekurzivnog potprograma u C/C++ da bi pronašli Fibonačijev broj:

int fib(int n)
{
	if(n<=1) return n;
	return fib(n-1)+fib(n-2);
}

Rani jezici kao što su Fortran nisu prvobitno podržavali rekurziju jer su varijable statički izdvojene, kao i lokacije za povratne adrese. Većina računara pre kasnih 1960-ih, kao što je PDP-8 nije imala podršku za hardverske skladišne registre.

Moderni jezici posle ALGOL poput PL/ 1 i C gotovo uvek koriste skladište, obično uz podršku najsavremenijim računarskim uputstvom postavljanim da obezbedi svež aktivicioni registar za svako izvršenje potprograma. Na taj način, ugneždeno izvršenje je slobodano da izmeni svoje lokalne promenljive bez ugroženosti za efekat na druge suspendovana izvršenja u toku. Kako se ugnieždeni poziva akumuliraju, poziv skladišne strukture je formiran i sastoji od jednog aktivacionog registra za svaki suspendovan potprograma. U stvari, skladišna struktura je praktično sveprisutna, i tako se registri aktiviranja obično nazivaju skladišni okviri.

Neki jezici, kao što su Pascal i Ada takođe podržavaju umetnute podprograme, koji su potprogrami koji se mogu pozvati samo u okviru spoljašnjeg (roditeljskog) potprograma. Unutrašnji podprogrami imaju pristup lokalnim promenljivim spoljašnjeg potprograma koji ih poziva. Ovo je postignuto čuvanjem dodatne tekstualne informacije u aktivacionom registru, takođe nazivanom ekran.

Ako potprogram može dobro da funkcioniše čak i kada je pozvan dok je drugo izvršenje već u toku, za taj potprogram se kaže da je ulazni. Rekurzivni potprogram mora biti ulazni. Ulazni podprogrami su takođe korisni u multi-tematskim situacijama, jer više tematski mogu pozvati isti potprogram bez straha od međusobnog ometanja. U IBM CICS sistemima za obradu transakcija, kvazi-ulazni je bio malo manje restriktivan, ali sličan, uslov za aplikacije koje su zajedničke za mnoge teme.

U multi-tematskom okruženju, obično postoji više od jednog skladišta. Okruženje koje u potpunosti podržava pomoćne radnje ili lenju evaluaciju može koristiti strukture podataka drugačije od skladišta da sačuva svoje aktivacione registe..

Preopterećenje uredi

U strogo kucanim jezicima, ponekad je poželjno da ima više funkcija sa istim imenom, ali da rade na različitim tipovima podataka, ili sa parametarima različitih profila. Na primer, funkcija kvadratni koren može se definisati da rade po realnim brojevima, složenim vrednostima ili matricama. Algoritam koji se koristi u svakom slučaju je različit, a rezultat može biti drugačiji. Pisanjem tri odvojene funkcije sa istim imenom, programer ima pogodnost da ne morat da pamti različita imena za svaku vrstu podataka. Dalje ako se podtip može da definisati za realne brojeve, da se odvoje pozitivni od negativnih realnih brojeva, dve funkcije mogu biti pisane za realne brojeve, jedna da se vrati pravi kad je parametar pozitivan, a druga da se vrati kompleksna vrednost kada je parametar negativan .

U objektno-orijentisanom programiranju, kada niz funkcija sa istim imenom može prihvatiti različite profile parametara ili parametre različitih tipova, za svaku od funkcija se kaže da je preopterećena.

Ovde je primer potprograma preopterećenja u C++:

#include <iostream>

double area(double h, double w) {
   return h * w;
}

double area(double r) {
   return r * r * 3.14;
}

int main() {
   double rectangle_area = area(3, 4);
   double circle_area = area(5);

   std::cout << "Area of a rectangle is " << rectangle_area << std::endl;
   std::cout << "Area of a circle is " << circle_area << std::endl;

   return 0;
}

U ovom kodu postoje dve funkcije istog imena, ali one imaju različite parametre.

Kao drugi primer, potprogram može da izgradi objekat koji će prihvatiti pravce, a pratiti njihov put do ovih tačaka na ekranu. Postoji mnoštvo parametara koji se mogu doneti u konstruktor (boja tragača, početne x i y koordinate, brzina tragača). Ako je programer hteo konstruktor da bude u stanju da prihvati samo parametar boje, onda je mogao da pozove drugu konstruktor koji prihvata samo boju, što zauzvrat poziva konstruktora sa svim parametrima koji prolaze u nizu difoltnih vrednosti za sve ostale parametre (X i Y će generalno biti usmereni na ekranu ili postavljeni na početak, a brzina će biti postavljena na drugu vrednost po izboru kodera).

Zatvaranja uredi

Zatvaranje je potprogram zajedno sa vrednostima neke od svojih varijabli iz sredine u kojoj je nastala. Zatvaranje su značajna karakteristika Lisp programskog jezika, koju je uveo Džon Makarti. U zavisnosti od primene, zatvarači mogu poslužiti kao mehanizam za sporedne efekte.

Konvencije uredi

Razvijen je veliki broj konvencija za kodiranje potprograma. Pored njihovog imenovanju, mnogi programeri su usvojili pristup da naziv potprograma treba da bude glagol kada obavlja određeni zadatak, pridev kada obavlja neku istragu, i imenicu kada se koristi da zameni varijable.

Neki programeri sugerišu da bi potprogram trebalo da obavlja samo jedan zadatak, a ako potprogram ne obavlja više od jednog zadatka, treba da bude podeljen na više potprograma. Oni tvrde da su podprogrami ključne komponente u održavanju koda, i njihove uloge u programu moraju ostati različite.

Zagovornici modularnog programiranja (modulacioni kod) zalažu se da svaki potprogram treba da ima minimalnu zavisnost od drugih delova koda. Na primer, upotreba globalnih promenljivih se generalno ne smatra mudrim rešenjem za ovu perspektivu, jer dodaje čvrstu spregu između potprograma i ovih globalnih promenljivih. Ako takva spojnica nije neophodna, ona preobražava potprograme umesto da prihvati date parametre. Međutim, povećanje broja parametara na potprogramima može uticati kod čitljivost.

Povratni kodovi uredi

Pored svog glavnog ili normalnog efekta, potprogram možda morati da obavesti pozivni program o posebnim uslovima koji su možda došli tokom njegovog izvršenja. U nekim jezicima i programskim standardima, to se često vrši putem povratnog kod, celobrojne vrednost postavljena od strane potprograma na nekom standardnom mestu, koji kodira normalne i posebne uslove.

U IBM System/360, gde se očekuje povratni kod iz potprograma, povratna vrednost je često dizajnirana da bude deljiva sa 4-tako da može da se koristi kao direktna filijala indeksa tabele u granu koja se često nalazi odmah nakon pozivnih instrukcija da se izbegnu dodatni uslovni testovi, što dodatno poboljšava efikasnost. U  System/360 assembly jeziku pisalo bi na primer:

           BAL  14,SUBRTN01    go to subroutine, storing return address in R14
           B    TABLE(15)      use returned value in reg 15 to index the branch table, 
*                              branching to the appropriate branch instr.
TABLE      B    OK             return code =00   GOOD                  }
           B    BAD            return code =04   Invalid input         } Branch table
           B    ERROR          return code =08   Unexpected condition  }

Optimizacija potprogramskih poziva uredi

Postoji značajna dužina trajanja preopterećenosti u pozivu potprograma, uključujući i prenošenje argumenata, grananje na potprograme, i grananje nazad u sagovornika. Preopterećenost često uključuje čuvanje i obnavljanje određenih registara procesora, izdvajanje i povratak memorije pozivnog rama, itd .. U nekim jezicima, svaki potprogramski poziv podrazumeva automatsko testiranje povratka koda potprograma, ili rukovanje izuzecima koji mogu da se pojave. U objektno-orijentisanim jezicima, značajan izvor reopterećenja je intenzivno korišćen dinamički obaveštenja za metodske pozive.

Postoje neke naizgled očigledne optimizacije postupnih poziva koje se ne mogu primeniti ako postupak može imati neželjene efekte. Na primer, u ekspresionom (f(x)-1)/(f(x)+1), funkcija f mora biti pozvana dva puta, jer dva poziva mogu vratiti različite rezultate. Pored toga, vrednost k mora biti ponovo preuzeta pre drugog poziva, budući da je prvi poziv možda promenio. Određivanje da li potprogram može imati neželjeni efekat je veoma teško (zapravo, neodlučno). Dakle, dok su ove optimizacije bezbedne u čisto funkcionalnim programskim jezicima, kompajleri tipičnog imperativnog programiranja obično moraju da pretpostave najgore.

Poravnjanje uredi

Metod koji se koristi za eliminisanje preopterećnosti je poravnjano proširenje ili poravnjanje tela potprograma na svakom mestu poziva (u odnosu na grananje na potprogram i nazad). Ne samo da se ovim izbegava preopterećenje poziva, nego i omogućava kompajleru da efikasnije optimizuje postupno telo uzimajući u obzir kontekst i argumente na tom pozivu. Ubačeno telo može biti optimizovano od strane kompajlera. Poravnjanje međutim, obično povećava veličinu koda, ukoliko program sadrži samo jedan poziv potprogramu, ili je potprogramsko telo manji kod nego preopterećen poziv .

Vidi još uredi

Reference uredi

  1. ^ U.S. Election Assistance Commission (2007). „Definitions of Words with Special Meanings”. Voluntary Voting System Guidelines. Arhivirano iz originala 08. 12. 2012. g. Pristupljeno 14. 01. 2013. 
  2. ^ Wheeler, D. J. (1952). „The use of sub-routines in programmes”. Proceedings of the 1952 ACM national meeting (Pittsburgh) on - ACM '52. str. 235. doi:10.1145/609784.609816. Arhivirano iz originala 28. 06. 2015. g. Pristupljeno 01. 01. 2017. 
  3. ^ Wilkes, M. V.; Wheeler, D. J.; Gill, S. (1951). Preparation of Programs for an Electronic Digital Computer. Addison-Wesley. 
  4. ^ Dainith, John. „"open subroutine." A Dictionary of Computing. 2004..”. Encyclopedia.com. Pristupljeno 14. 01. 2013. 
  5. ^ Knuth, Donald E. The Art of Computer Programming, Volume I: Fundamental Algorithms. Addison-Wesley. ISBN 978-0-201-89683-1. 
  6. ^ O.-J. Dahl, E. W.; Dijkstra; C. A. R. Hoare (1972). Structured Programming. Academic Press. ISBN 0-12-200550-3. 
  7. ^
  8. ^ „ARM Information Center”. Infocenter.arm.com. Pristupljeno 29. 09. 2013. 
  9. ^ „Overview of x64 Calling Conventions”. Msdn.microsoft.com. Arhivirano iz originala 03. 12. 2013. g. Pristupljeno 29. 09. 2013. 
  10. ^ „Function Types”. Msdn.microsoft.com. Pristupljeno 29. 09. 2013.