Podprogram [pòtprográm] je v računalništvu zaporedje programskih ukazov, ki izvaja določeno nalogo, zbrano kot enota. Ta enota se lahko potem rabi v programih kjerkoli se mora ta določena naloga izvesti.
Podprogrami so lahko določeni znotraj programov ali ločeno v programskih knjižnicah in jih lahko uporabijo drugi programi. V različnih programskih jezikih se podprogrami lahko imenujejo pòdrutȋna, rutȋna, fúnkcija, metóda ali procedúra. Tehnično imajo ti izrazi različne definicije in izrazje se od jezika do jezika razlikuje. Včasih se rabi tudi še splošnejši, krovni izraz klicljìva enôta (callable unit).
Ime podprogram nakazuje, da se takšna enota obnaša na skoraj enak način kot računalniški program, ki se rabi kot en korak v večjem programu ali drugem podprogramu. Podprogram se velikokrat sprogramira tako, da se lahko med izvajanjem programa požene večkrat in z več mest, vključno iz drugih podprogramov, in po klicu, ko je naloga podprograma zaključena, preide nazaj (se vrne) k naslednjemu ukazu. Podporogram si je začetno zamislil John Mauchly med svojim delom na računalniku ENIAC in ga predstavil na harvardskem simpoziju »Preparation of Problems for EDVAC-type Machines« januarja 1947. Za formalno iznajdbo tega koncepta veljajo Maurice Vincent Wilkes, David John Wheeler in Stanley Gill. Imenovali so ga zaprti podprogram, v nasprotju z odprtim podprogramom ali makrojem. Tudi Turing je že obravnaval podprograme v članku leta 1945 o konstrukciji in predlogih za stroj ACE britanskega Narodnega fizikalnega laboratorija in je šel tako daleč, da je izumil koncept sklada povratnega naslova (return address stack).
Podprogrami so močno programsko orodje in skladnja mnogih programskih jezikov vsebuje podporo za njihovo pisanje in uporabo. Razumna raba podprogramov, na primer prek pristopa strukturalnega programiranja, bo velikokrat zelo omejila ceno razvoja in vzdrževanja velikega programa, ter povečala njegovo kakovost in zanesljivost. Podprogrami, velikokrat zbrani v knjižnicah, so pomemben mehanizem za deljenje in prodajo programske opreme. Disciplina objektno usmerjenega programiranja temelji na objektih in metodah, ki so podprogrami, pripojeni tem objektom ali razredom objektov.
Pri prevajanju se metoda imenuje povezana koda (threaded code), izvršni program pa je v bistvu zaporedje podprogramskih klicev.
Glavni koncepti
Vsebina podprograma je njegovo telo, ki je del programske kode, in ta se pri klicu podprograma izvede.
Podprogram se lahko napiše tako, da od klicočega programa pričakuje eno ali več podatkovnih vrednosti – da nadomesti njegove parametre (postavke) ali formalne parametre. Klicoči program zagotovi dejanske vrednosti teh parametrov, ki se imenujejo argumenti. Različni pogramski jeziki lahko rabijo različne dogovore za prenosne argumente: dogovor opis splošna raba klic po vrednosti Argument se ovrednoti in kopija vrednosti se prenese na podprogram Privzeto v večini jezikov podobnih ALGOLu po različici ALGOL 60, kot so paskal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada in mnogi drugi. C, C++, java (sklici na objekte in polja se tudi prenašajo po vrednosti) klic po sklicevanju Sklicevanje na argument, tipično je prenesen njegov naslov Izbirno v večini jezikov podobnih ALGOLu pa različici ALGOL 60, kot so ALGOL 68, paskal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada in mnogi drugi. C++, Fortran, PL/I klic po rezultatu Vrednost parametra se kopira nazaj na argument pri vračanju iz podprograma Ada – parametri OUT klic po vrednosti-rezultatu Vrednost parametra se kopira nazaj na vhod podprograma in spet pri vračanju ALGOL, Swift – parametri in-out klic po imenu Kot makro – nadomesti parametre z neovrednotenim izrazom argumenta, nato ovrednoti argument v kontekstu klicočega vsakokrat, ko ga klicani podprogram uporabi. ALGOL, Scala klic po konstantni vrednosti Podobno kot klic po vrednosti, razen, da se parameter obravnava kot konstanta PL/I – parametri NONASSIGNABLE, Ada – parametri IN
Podprogramski klic ima lahko tudi stranskje učinke, ko je na primer spreminjanje podatkovnih struktur v računalniškem pomnilniku, branje ali pisanje na zunanjo napravo, tvorjenje datoteke, zaustavljanje programa ali stroja ali celo zakasnitev izvrševanja programa za določen čas. Podprogram s stranskimi učinku lahko vrne različne rezultate vsakič, ko je klican, tudi, če je klican z enakimi argumenti. Zgled je podprogram za naključna števila, ki je na voljo v mnogih jezikih, in, ki pri vsakem klicu vrne različno psevdonaključno število. Široka raba podprogramov s stranskimi učinki je značilna za imperativne jezike.
Podprogramu so lahko sprogramirani tako, da lahko za izvedbo naloge rekurzivno kličejo sami sebe na enem ali več mestih. Ta metoda omogoča neposredno implementacijo funkcij, ki so definirane z matematično indukcijo in rekurzivnimi algoritmi deli in vladaj.
Podprogram, katerega namen je izračunati funkcijo z Boolovo vrednostjo, to je odgovor na vprašanje z da ali ne, se včasih imenuje predikat. V jezikih logičnega programiranja se vsi podprogrami imenujejo predikati, ker prvenstveno določajo uspeh ali neuspeh.
Podprogram, ki ne vrača nobene vrednosti ali vrača ničelno vrednost, se včasih imenuje procedura. Procedure po navadi spreminjajo svoje argumente in so bistveni del proceduralnega programiranja.
Funkcije
Funkcija je podprogram, ki vrne vrednost. Glavni namen funkcij je razbiti zapletene izračune v smiselne kose in jih poimenovati. Podprogram lahko vrne izračunano vrednost klicočemu (njegovo povratno vrednost), ali pa da različne vrednosti rezultatov ali izhodne parametre. Splošna raba podprogramov je res implementacija funkcij v katerih je namen podprograma zgolj izračun enega ali več rezultatov, katerih vrednosti so v celoti določene z argumenti, podanimi podprogramu. Taka zgleda sta na primer izračun logaritma števila ali determinanta matrike. V nekaterih jezikih je skladnja za podprogram, ki vrača vrednost, dejansko enaka skladnji za podprogram, ki ne vrača vrednosti, razen za odsotnost na primer stavka RETURNS. V nekaterih jezikih lahko podprogram dinamično izbere ali vrne vrednost ali je ne vrne, kar je odvisno od njegovih argumentov.
Podpora jezikov
Visokonivojski programski jeziki po navadi vsebujejo določene konstrukte za: razmejitev dela programa (telesa), ki tvori podprogram dodelitev identifikatorja (imena) podprogramu označitev imen in podatkovnih tipov njegovih parametrov in povratnih vrednosti zagotovitev zasebnega imenskega dosega njegovih začasnih spremenljivk istovetnost spremenljivk zunaj podprograma, ki so v njem dosegljive klicanje podprograma zagotovitev vrednosti njegovih parametrov glavni program, ki naj vsebuje njegov naslov podprogram, ki naj vsebuje naslov naslednjega ukaza za klic funkcije v glavnem programu označitev povratnih vrednosti iz notranjosti njegovega telesa vračanje h klicočemu programu razmestitev vrednosti, ki jih vrne klic upravljanje z izjemami, ki se srečajo med klicem pakiranje podprogramov v modul, knjižnico, objekt ali razred
Nekateri programski jeziki, kot so paskal, Fortran, Ada in mnoga narečja BASICa, razlikujejo med funkcijami ali funkcijskimi podprogrami, ki zagotavljajo eksplicitno povratno vrednost klicočemu programu, in podprogrami ali procedurami, ki te vrednosti ne zagotavljajo. V teh jezikih so klici funkcij normalno vgrajeni v izraze – na primer funkcija sqrt se lahko kliče kot y = z + sqrt(x). Klici podprogramov se lahko obnašajo skladenjsko kot stavki – prodprogram print se lahko kliče kot if x > 0 then print(x) ali pa jih eksplicitno kličejo stavki, kot na primer CALL ali GOSUB – na primer call print(x)). Drugi jeziki, kot sta C in lisp, med funkcijami in podprogrami ne razlikujejo.
V jeziki strogega funkcionalnega programiranja, kot je Haskell, so lahko podprogrami brez stranskih učinkov, kar pomeni, da se raznolična notranja stanja programa ne bodo spremenila. Funkcije bodo vedno vrnile enak rezultat, če se kličejo večkrat z enakimi argumenti. Takšni jeziki tipično podpirajo le funkcije, ker so podprogrami, ki ne vračajo vrednosti, neuporabni vse dokler ne povzročijo stranskega učinka.
V programskih jezikih, kot so C, C++ in C#, se podprogrami lahko imenujejo preprosto funkcije, kar pa se ne sme zamenjevati s pojmoma funkcije v matematiki ali funkcionalnim programiranjem, ki sta različna koncepta.
Prevajalnik jezika bo po navadi prevedel klice podprogramov in povratne vrednosti v strojne ukaze glede na dobro definiran dogovor o klicanju, tako da se lahko podprogrami prevedejo ločeno od programov, ki jih kličejo. Zaporedja ukazov, ki odgovarja klicnim in povratnim stavkom, se imenuje uvod in sklepna beseda podprograma.
V zelo zgodnjih zbirnikih je bila podpora podprogramov omejena. Podprogrami eksplicitno niso bili ločeni drug od drugega ali od glavnega programa, in res se je lahko izvorna koda pomešala s tisto iz drugih programov. Nekateri zbirniki so ponujali preddefinirane makroje za tvorjenje zaporedij klicev in vračanj. Do 1960-ih so imeli zbirniki po navadi razvitejšo podporo tako za vrinjene kot za ločeno zbrane podprograme, ki se jih je dalo povezati skupaj.
Eden od prvih programskih jezikov, ki je podpiral uporabniško napisane podprograme, je bil FORTRAN II. Prevajalnik za IBM FORTRAN II je izšel leta 1958. Tudi ALGOL 58 in drugi zgodnji programski jeziki so podpirali proceduralno programiranje.
Prednosti
Prednosti razbitja programa na podprograme so: dekompozicija zapletenih programskih nalog v preprostejše korake – to je eden od dveh glavnih orodij strukturalnega programiranja poleg podatkovnih tipov zmanjšanje podvojitev kode znotraj programa usposobitev ponovne rabe kode prek večkratnih programov ločitev velike programske naloge med različne programerje ali različne stopnje projekta skrivanje podrobnosti izvedb od uporabnikov podprograma izboljšanje berljivosti kode z zamenjevanjem bloka kode s klicem funkcije, kjer opisno ime funkcije služi za opis bloka kode. Zaradi tega je klicna koda zgoščena in berljiva, četudi funkcija ni namenjena ponovni rabi. izboljšanje sledljivosti – večina jezikov ponuja načine za dosego sledi klica, kar vsebuje imena vključenih podprogramov in mogoče še več informacij, kot so imena dototek in številke vrstic. Če se koda ne bi razstavila v podprograme, bi bilo razhroščevanje zelo oteženo.
Slabosti
V primerjavi z rabo vrinjene kode klicanje podprograma v klicnem mehanizmu vsiljuje nekaj računalniške režije.
Podprogram tipično zahteva standardno vzdrževalno kodo – tako pri vhodu in pri izhodu iz funkcije (uvod in sklepna beseda funkcije), kar po navadi registre za splošni namen in povratni naslov ohranja v najmanjšem obsegu.
Zgodovina
Zamisel podprograma je bila uresničena po tem, ko so računski stroji nekaj časa že obstajali. Ukazi aritmetičnih in pogojnih skokov so bili načrtovani v naprej in so se relativno malo spremenili, posebni ukazi za podprogramske klice pa so se skozi leta zelo spremenili. Najzgodnejši računalniki in mikroprocesorji, kot sta na primer Manchester Baby in RCA 1802, niso imeli enega ukaza za podprogramski klic. Podprogrami so se lahko izvedli, vendar so zahtevali programerje, ki so rabili klicno zaporedje – niz ukazov pri vsakem klicnem mestu.
Podprogrami so bili izvedeni v računalniku Z4 Konrada Zuseja leta 1945.
Leta 1945 je Alan Turing rabil izraz »skriti« (bury) in »ne skriti« (unbury) v smislu klicanja in vračanja iz podprogramov.
Januarja 1947 je John William Mauchly predstavil splošne opombe na Simpoziju o digitalnih računskih strojih velikega obsega pod skupnim pokroviteljstvom Univerze Harvard in Urada za oborožitev VM ZDA. Tu je razpravljal o zaporednih in vzporednih operacijah ter predlagal: