У програмирању, потпрограм је низ програмских инструкција које обављају одређени задатак, упаковане као јединица. Ова јединица се затим може користити у програмима кад год треба извести тај конкретан задатак. Потпрограми се могу дефинисати у оквиру програма, или одвојено у библиотекама, које се могу искористити у разноврсним програмима. У различитим програмским језицима, потпрограм се може назвати: процедура, функција, рутина, метод или потпрограм. Генерички термин позивна јединица се понекад користи.[1]

Као што само име потпрограма сугерише, потпрограм се понаша на исти начин као и рачунарски програм који се користи као један корак ширег програма или другог потпрограма. Потпрограм је често кодиран тако да може стартовати (позвати) неколико пута и са више места у току једног извршења програма, и из других потпрограма, а затим се преспоји назад (повратак) на следеће инструкције након позива чим је задатак потпрограма завршен. Морис Вилкис, Дејвид Вилер, а Стенли Гил су задужени за проналазак овог концепта, који су назвали затворени потпрограм,[2][3] насупрот отвореном потпрограму или макроу.[4]

Потпрограми су моћан програмски алат,[5] и синтакса многих програмских језика укључује подршку за писање и коришћење. Разумно коришћење потпрограма (на пример, кроз структурирано програмирање приступа) често значајно смањи трошкове развоја и одржавања великог програма, док је повећан квалитет и поузданост.[6] Потпрограми, често прикупљени у библиотекама, представљају важан механизам за дељење и трговање софтвера. Дисциплина објектно-оријентисано програмирање је базирана на објектима и методама (који су потпрограмима везани за ове објекте или класе објеката).

У поступку комбиновања метод назван третирани код, извршни програм је у основи низ подрутинских позива.

Основни појмови

уреди

Садржај једног потпрограма је његово тело, које је део програмског кода који се извршава када се потпрограм зове или призива.

Потпрограм може бити написан тако да очекује да ће добити једну или више вредности података из позивајућег програма (његове параметре или формалних параметара). Расписивање програм пружа стварне вредности за ове параметре, који се зову аргументи. Различити програмски језици могу користити различите конвенције за доношење аргумената

Конвенција Опис Заједничка употреба
Позив по вредности Аргумент је оцењен и копија вредности је прослеђена

потпрограму

Уобичајено већина Алгол језика после Алгол 60, попут Паскала, Делфија, Симула, CPL, PL/M, Modula, Oberon, Ada, и многих других.
Позив по референци Позивање на аргументе, обично се преноси његова адреса Избор код већине Algol језика после Algol 60, попут Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, и многих других. C (структуре и низови), C++, Fortran, PL/I
Позив по резултату Параметар вредност је копиран назад у аргумент на повратку из потпрограма Ada OUT параметри
Позив по вредности-резултату Параметар вредност је копиран назад на улазу у потпрограм и опет у повратку Algol
Позив по имену Као макро - мења параметре са безвредним аргументима израза Algol, Scala
Позив по константној вредности Као позив по вредности само што се тај параметар посматра као константа PL/I NONASSIGNABLE параметри, Ada IN параметри

Подрутина може вратити израчунате вредности на својој колер (његове повратне вредности), или да обезбеди разне резултате вредности или излазне параметре. Заиста, уобичајена употреба потпрограма је да спроведе математичке функције, у којима је сврха потпрограма само да израчуна један или више резултата чије вредности се у потпуности одређују параметрима у потпрограма. (Примери могу укључити израчунавању логаритам броја или детерминанте матрице.)

Потпрограм позива може имати нежељене ефекте као што су мењање структуре података у меморији рачунара, читајући или пишући на периферне уређаје, креирање датотеке, заустављање програма или машине, или чак одлагање извршења програма за одређено време. Потпрограм са споредним ефектима може да врати различите резултате сваки пут када се зове, чак и ако то се зове са истим аргументима. Пример је случајна функција број, доступан на многим језицима, да врати различите псеудо случајне бројеве сваки пут када се зове. Широка употреба потпрограма са нежељеним ефектима је карактеристична за императивне програмске језике.

Потпрограм може бити кодиран тако да може да се позове рекурзивно, на једном или више места, да изврши свој задатак. Овај метод омогућава директну примену функција дефинисаних математичким индукцијама и рекурзивним подели па владај алгоритмима.

Потпрограм чија је сврха да се израчуна једна вредност функције (то јест да одговорите на питање да/не) се понекад назива предикат. У логици програмских језика, често све потпрограме зову предикатима, јер су пре свега одређују успех или неуспех. На пример, било који тип функције је рутина, али не и главна.

Језичка подршка

уреди

Програмски језици високог нивоа обично укључују специфичне конструкције:

  • ограничавање део програма (тела) који сачињава потпрограм
  • додељивање идентификатора (име) потпрограму
  • навођење имена и типова података параметара и вредности за повратак
  • лично именовање простора за привремене променљиве
  • идентификацију променљиве изван потпрограма који су доступни у њему
  • позивање потпрограма
  • додељивање вредности параметрима
  • навођење повратних вредности из свог тела
  • повратан на позивајући програм
  • располагање вредностима враћеним позивом
  • решавање свих изузетака са којима се срећу током позива
  • паковање потпрограма у модуле, библиотеке, објекте, класе, итд.

Неки програмски језици, као што су Pascal, Fortran, Ada и многи дијалекти Basic, разликују функције или функције потпрограма, који пружају изричиту повратну вредност у позивајући програм, и потпрограма или процедуре, које не. У тим језицима, функције позиви су обично уграђени у изразима (на пример sqrt може бити позвана са y = z + sqrt(x)). Функција се понаша исто синтаксички као изјава (на пример print процедура може бити позвана са if x > 0 then print(x) или се експлицитно позива као CALL или GOSUB (на пример call print(x) Други језици као што су C и Lisp, не праве разлику између функција и потпрограма.

У строго функционалним програмским језицима као што су Haskell, потпрограм може имати никакве нежељених ефеката, што значи да се различити интерни делови програма неће променити. Функције ће увек вратити исти резултат ако више пута зовете са истим аргументима. Такви језици обично подржавају само функције, јер потпрограми које не враћају вредност немају користи, осим ако они могу изазвати споредне ефекте.

У програмским језицима као што су С, С++ и С #, Потпрограми такође могу једноставно да се називају функције, не треба мешати са математичким функцијама или функционалним програмирањем, који су различити концепти.

Преводилац језика ће обично превести процедуру позива и повратак у упутства машине у складу са добро дефинисаном позивном конвенцијом (конгресом), тако се да потпрограми могу превести одвојено од програма који их позивају. Наставне секвенце које одговарају позиву и повратним изјавама називају се поступни увод и епилог.

Предности

уреди

Предности раздвајања програма у потпрограме укључују:

  • Разбијање комплексног задатка програмирања у једноставније кораке: ово је један од два главна алата структурног програмирања, заједно са структурама података
  •  Смањење дупликата кода у оквиру програма
  • Омогућава поновну употребу кода преко више програма
  • Подела великог програмерског задатка различитим програмерима, или различитим фазама пројекта
  • Скривање детаља имплементације од корисника потпрограма
  • Побољшање следљивости (то јест већина језика нуди начине да се дође до трага позива који укључује имена укључених потпрограма, а можда чак и до више информација попут имена датотека и бројева линија); без разлагања кода у потпрограме, отклањање грешака било би немогуће.

Недостаци

уреди

Позивајући се на потпрограм (насупрот коришћењу in-line кода) намеће нека рачунарска преоптерећења у позивном механизму.

Потпрограм обично захтева стандардан административни код - како на улазу тако и на излазу функције (функција увода и епилога - обично штеди опште регистре сврхе и враћа адресу као минимум).

Историја

уреди

Идеја о потпрограма је почела да функционише након што су рачунарске машине постојале већ неко време. Аритметичке и условне инструкције раста планиране су унапред и да промениле су се релативно мало; али посебна упутства се користе за процедурне позиве су се веома много променила током година. Најранији рачунари и микропроцесори, као што је Експериментална машина мањег обима и RCA 1802( Small-Scale Experimental Machine и RCA 1802,), није имала ниједну инструкцију потпрограмског позива. Потпрограми су могли бити реализовани, али је потребно да програмери користе позивне секвенце - низ инструкција - на свакој локацији позива. Неки врло стари рачунари и микропроцесори, као што су IBM 1620, Intel 8008, и PIC, имају једну инструкцију потпрограмског позива који користи наменски хардверско складиште за чување повратне адресе - овакав хардвер подржава само неколико нивоа угњеждених потпрограма, али може да подржи рекурзивне потпрограме. Машине пре средине 1960-их - као што је UNIVAC I, PDP-1, и IBM 1130 - обично користе позивну конвенцију која је сачувала бројач са упутством у првој меморијској локацији под називом потпрограма. Ово омогућава произвољно дубоке нивое угњеждене потпрограме, али не подржава рекурзивне потпрограме. PDP-11 (1970) је један од првих рачунара са потпрограмском позивном инструкцијом са складиштењем; Ова функција подржава произвољно дубоко угњеждене потпрограме и подржава рекурзивне потпрограме.

Језичка подршка

уреди

У раним монтажама, подршка потпрограма била је ограничена. Потпрограми нису експлицитно одвојени једни од других или од главног програма, и заиста изворни код једног потпрограма може се смењивати са оним другим кодом потпрограма. Неки монтери ће понудити предефинисане макрое за генерисање позива и враћање секвенце. До 1960-их, монтери су обично имали много софистициранију подршку и за спојене и одвојене окупљене потпрограме који би могли бити повезани заједно.

Потпрограмске библиотеке

уреди

Чак и са овим незграпним приступом, потпрограми су се показали веома корисним. Због једне ствари су дозволили исти код који се користи у многим различитим програмима. Меморија је била врло оскудан ресурс на старим рачунарима, а Потпрограми дозвољавају значајне уштеде у величини програма.

У многим старим рачунарима, упутства програма су ушла у меморију од папирне траке. Сваки потпрограм је тада могао бити обезбеђен посебним комадом траке, утоварен или спојен пре или после главног програма; а иста потпрограм трака би тада могла да се користи од стране многих различитих програма.[7] Сличан приступ је коришћен у рачунарима чији је главни улаз кроз бушене картице. Име потпрограм библиотека је првобитно значило библиотеку, у дословном смислу, која чува индексиране колекције таквих трака или картица за колективно коришћење.

Повратак индиректним скоком

уреди

Да бисте уклонили потребу за само-модификацију кода, рачунарски дизајнери су најзад обезбедили посредну скок инструкцију, чији операнд је, уместо да буде повратна адреса, био локација на променљиве или процесор регистар који садржи повратну адресу.

На тим рачунарима, уместо мењања повратног скока потпрограма, позивни програм би сачувао повратне адресе у променљивој, тако да када се потпрограм заврши, изврши индиректан скок - непосредно извршење на локацији коју је дао унапред дефинисаном променљивом.

Скок на потпрограм

уреди

Други напредак је био скок у потпрограмски инструкцију, који је комбиновао уштеду повратне адресе са позивним скока, чиме се значајно минимизира преоптерећење.

У IBM System/ 360, на пример, упутства филијале BAL или BALR, намењена за позивни поступак, би сачували повратне адресе у регистру процесора наведеног у упутству. Да бисте се вратили, потпрограм је само требало да изврши индиректно преспајање инструкцију преко тог регистра. Ако потпрограм треба регистрар за неку другу сврху (као што је позивање још једног потпрограма), то би сачувало садржај регистра на приватну меморијску локације или регистарско складиште.

У системима као што су HP 2100 је JSB инструкција би извршила сличан задатак, осим што је повратна адреса чува у меморијској локацији која је била мета гране. Извршење поступка би заправо почело на наредној меморијској локацији. У HP 2100 монтажном језику писаће на пример

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

да позове потпрограм назван MYSUB из главног програма. Потпрограм ће бити кодирани као

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

JSB инструкција је поставила адресу следеће инструкције (именована као BB) у локацији која је наведена као свој операнд (именован као MYSUB), а затим је разгранала на следећу локацију након тога (тј АА = МYSUB + 1). Потпрограм је онда могао да се врати у главни програм извршавањем индиректног скока ЈМP МYSUB, који се разгранао до локације чуване на локацији МYSUB.

Компајлери за Fortran и друге језике су могли лако да искористе ова упутства кад су на располагању. Овај приступ подржао је више нивоа позива; Међутим, пошто су повратна адреса, параметри и повратне вредности потпрограма додељени фиксној меморијској локацији, то није дозвољавало рекурзивне позиве.

Иначе, сличан метод је коришћен од стране Lotus 1-2-3, у раним 1980-их, да се открије прерачунате зависности у табели. Наиме, локација је била резервисана у свакој ћелији да сачува повратну адресу. Пошто кружне референце нису дозвољен за природни прерачун, то омогућава слободну шетњу без резервисања простора за складиште у меморији, која је веома ограничено на малим рачунарима као што је IBM PC.

Складиште позива

уреди

Већина модерних имплементација користи Call stack, посебан случај структуре складишта података, да спроведе потпрограмски позива и повратак. Сваки постепени позив креира нову ставку која се зове стек(складишни) оквир, на врху гомиле; када се поступак врати, њен стек оквир се брише из гомиле, а њен простор може се користити и за друге поступне позиве. Сваки стек оквир садржи приватне податке одговарајућег позива, што обично укључује поступне параметре и унутрашње варијабле, као и повратну адресу.

Позивна секвенца може да се реализује низом обичних упутстава (приступ који се још увек користи у смањеном сету инструкција рачунарства (RISC) и веома дугим инструкцијама ворда (VLIW) архитектуре), али многе традиционалне машине дизајниране од краја 1960-их су укључили посебна упутства за ту сврху.[8][9][10]

Складишни позив се обично спроводи као суседно подручје меморије. То је произвољно дизајниран избор, било да је дно складишта најнижа или највиша адреса у овој области, тако да складиште може да унапред или уназад расте у меморији; Међутим, многе архитектуре су изабрале ово друго.

Неки пројекти, нарочито неке четврте имплементације, користе две одвојене гомиле, једна углавном за управљачке информације (као што су повратак адресе и петље бројаче), а други за податке. Бивши је, или је радио као, складишни позив и да је само индиректно доступан за програмера путем других језичких конструкција, док је други био више директно доступан.

Када су први пут уведени процедурни позиви базирани на складишту, важан мотив је био да се спасе драгоцена меморију. У овој шеми, компајлер не мора да резервише посебан простор у меморији за приватне податке (параметре, повратну адресу, и локалне варијабле) сваког поступка. У сваком тренутку, стек садржи само приватне податке о позивима који су тренутно активни (наиме, који су позвани, али се још увек нису вратили). Због начина на који су програми обично састављени од библиотека, било је (и још увек је) необично да се пронађу програме који укључују хиљаде потпрограма, од којих је само неколицина активна у сваком тренутку. За такве програме, позивни стек механизам је могао спасити значајне количине меморије. Заиста, позивни стек механизам се може сматрати као најранији и најједноставнији начин за аутоматско управљање меморијом.

Међутим, још једна предност методе складишног позива је што омогућава рекурзивне потпрограмске позиве, јер сваки угнежден позив истог поступка добија посебно складиште својих приватних података.

Одложено складиштење

уреди

Један од недостатака у Call stack механизма су повећани трошкови поступног позива и његовог повратка. Додатни трошкови укључују да се увећава и умањује стек показивач (и, у неким архитектурама, проверава прегомилавање складишта), и приступање локалним променљивим и параметрима релативних адреса, уместо апсолутних адреса. Трошкови се могу реализовати повећањем времена извршења, односно повећањем сложеност процесора, или обоје.

Ово изнад је најочигледније и непожељно у поступцима листа или функцијама листа, који се враћају без икаквих процедурних позива

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

C и C++ примери

уреди

У C и C++програмским језицима, потпрограми се називају функције (или функција чланица када је повезан за класе). Ови језици користите посебне кључне речи да указују на то да функција не узима никакве параметре (нарочито у C ) или не враћају никакву вредност. Имајте на уму да је C и C++ функције могу имати нежељене ефекте, укључујући мењање било које променљиве чије адресе су прошле као параметри (тј односно као референце). Примери:

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

Функција не враћа вредност и мора да буде позвана као самостална функција, нпр., function1();

 int function2(void)
 {
     return 5;
 }

Ова функција враћа резултат (број 5), а позив може бити део израза, нпр., x + function2()

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

Ова функција конвертује број између 0 до 6 у почетном слову одговарајућег дана у недељи, односно 0 до 'S', 1 на 'М', ..., 6 на 'S'. Резултат позивања може се доделити променљивој, на пример, num_day = function3(number);.

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

Ова функција не враћа вредност, али мења променљиву чија адреса је прошла као параметар; била би позвана као "function4(&variable_to_increment);".

Visual Basic 6 examples

уреди

У Visual Basic 6 језику потпрограми се називају функције. Visual Basic 6 користи различите термине назване типовима који дефинишу шта је све дато као параметар. По правилу једна неодређена променљива је регистрована као тип варијанте и може бити усвојена као ByRef или ByVal. Такође, када се проглашава функција, наводи се јавна, приватна или пријатељ ознака, која одређује да ли се може приступити изван модула или пројекта.

  • По вредности [ByVal] – начин доношења вредности аргумента у поступку, уместо доношења адресе. Ово омогућава поступак приступа копији променљиве. Као резултат тога, стварна вредност једне променљиве не може се мењати поступком који је донет.
  •   По референци [ByRef] – начин доношења адресе аргумента за процедуре, уместо доношења вредности. Ово омогућава поступак приступа стварној променљивој. Као резултат тога, стварна вредност једне променљиве може се заменити поступком кроз који је прошла. Уколико није другачије назначено, аргументи су прошли по референци.
  • Јавни (опциони) - указује на то да поступак функција је доступан свим другим поступцима у свим модулима. Ако се користи у модулу који садржи опцију приватно, поступак није доступан ван пројекта.
  • Приватно (опциони) - указује на то да поступак функција је доступан само другим поступцима у модулу где је декларисан.
  • Пријатељ (опциони) - користи се само у класи модула. Указује на то да је поступак функција видљив током целог пројекта, али није видљиво за контролора инстанце објекта.
    Private Function Function1()
        ' Some Code Here
    End Function
    
    Функција  не враћа вредност и мора бити позвана као самостална функција, на пример, Function1
    Private Function Function2() as Integer
        Function2 = 5
    End Function
    
    Ова функција враћа резултат (број 5), а позив може бити део израза, на пример, 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
    
    Ова функција конвертује број између 0 до 6 у почетном слову одговарајућег дана у недељи, односно 0 до 'М', 1 на 'М', ..., 6 на 'S'. Резултат позивања може се доделити променљивој, на пример, num_day = Function3(number).
    Private Function Function4(ByRef intValue as Integer)
        intValue = intValue + 1
    End Function
    
    Ова функција не враћа вредност, али мења променљиву чија адреса је прошла као параметар; била би позвана као "Function4(variable_to_increment)".

PL/I пример

уреди

PL/I сам поступак позива може бити усвојен на дескриптор који пружа информације о аргументу, као што су дужина жица и границе. Ово омогућава да поступак буде општи и елиминише потребу да програмер прође такве информације. По дифолту PL/I пушта аргументе као референце. А (тривијално) потпрограм да промените знак сваког елемента дводимензионалног низа може да изгледа овако:

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

То би се могло позвати различитим низовима као што следи:

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

Локалне променљиве, рекурзија и поновни улази

уреди

Потпрограм може сматрати корисним да искористи одређене количине огреботина простора; то јест, меморија се користи приликом извршења тог потпрограма да одржи међурезултате. Променљиве које се чувају у овом простору гребања се називају локалне променљиве, а простор гребање се назива рекорд за активирање. Рекорд активација обично има повратну адресу која говори где да прође контролу када се потпрограм заврши.

Потпрограм може имати било који број и природу позива сајтовима. Ако је подржана рекурзија, потпрограм можда чак и себе да зове, изазивајући обуставу његовог извршења док се други потпрограм јавља. Рекурзија је корисно средство за поједностављивање неких сложених алгоритама, и разбија сложене проблеме. Рекурзивни језици генерално дају нову копију локалних променљивих на сваки позив. Ако програмер жели вредност локалних променљивих да остану исте између позива, могу се прогласити статичним у неким језицима, односно глобалне вредности или заједничке просторије могу да се користе. Овде је пример рекурзивног потпрограма у C/C++ да би пронашли Фибоначијев број:

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

Рани језици као што су Fortran нису првобитно подржавали рекурзију јер су варијабле статички издвојене, као и локације за повратне адресе. Већина рачунара пре касних 1960-их, као што је PDP-8 није имала подршку за хардверске складишне регистре.

Модерни језици после АLGOL попут PL/ 1 и C готово увек користе складиште, обично уз подршку најсавременијим рачунарским упутством постављаним да обезбеди свеж активациони регистар за свако извршење потпрограма. На тај начин, угнеждено извршење је слободно да измени своје локалне променљиве без угрожености за ефекат на друге суспендована извршења у току. Како се угнеждени позива акумулирају, позив складишне структуре је формиран и састоји од једног активационог регистра за сваки суспендован потпрограма. У ствари, складишна структура је практично свеприсутна, и тако се регистри активирања обично називају складишни оквири.

Неки језици, као што су Pascal и Ada такође подржавају уметнуте потпрограме, који су потпрограми који се могу позвати само у оквиру спољашњег (родитељског) потпрограма. Унутрашњи потпрограми имају приступ локалним променљивим спољашњег потпрограма који их позива. Ово је постигнуто чувањем додатне текстуалне информације у активационом регистру, такође називаном екран.

Ако потпрограм може добро да функционише чак и када је позван док је друго извршење већ у току, за тај потпрограм се каже да је улазни. Рекурзивни потпрограм мора бити улазни. Улазни потпрограми су такође корисни у мулти-тематским ситуацијама, јер више тематски могу позвати исти потпрограм без страха од међусобног ометања. У IBM CICS системима за обраду трансакција, квази-улазни је био мало мање рестриктиван, али сличан, услов за апликације које су заједничке за многе теме.

У мулти-тематском окружењу, обично постоји више од једног складишта. Окружење које у потпуности подржава помоћне радње или лењу евалуацију може користити структуре података другачије од складишта да сачува своје активационе регистре..

Преоптерећење

уреди

У строго куцаним језицима, понекад је пожељно да има више функција са истим именом, али да раде на различитим типовима података, или са параметрима различитих профила. На пример, функција квадратни корен може се дефинисати да раде по реалним бројевима, сложеним вредностима или матрицама. Алгоритам који се користи у сваком случају је различит, а резултат може бити другачији. Писањем три одвојене функције са истим именом, програмер има погодност да не мора да памти различита имена за сваку врсту података. Даље ако се подтип може да дефинисати за реалне бројеве, да се одвоје позитивни од негативних реалних бројева, две функције могу бити писане за реалне бројеве, једна да се врати прави кад је параметар позитиван, а друга да се врати комплексна вредност када је параметар негативан .

У објектно-оријентисаном програмирању, када низ функција са истим именом може прихватити различите профиле параметара или параметре различитих типова, за сваку од функција се каже да је преоптерећена.

Овде је пример потпрограма преоптерећења у 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;
}

У овом коду постоје две функције истог имена, али оне имају различите параметре.

Као други пример, потпрограм може да изгради објекат који ће прихватити правце, а пратити њихов пут до ових тачака на екрану. Постоји мноштво параметара који се могу донети у конструктор (боја трагача, почетне x и y координате, брзина трагача). Ако је програмер хтео конструктор да буде у стању да прихвати само параметар боје, онда је могао да позове другу конструктор који прихвата само боју, што заузврат позива конструктора са свим параметрима који пролазе у низу дифолтних вредности за све остале параметре (X и Y ће генерално бити усмерени на екрану или постављени на почетак, а брзина ће бити постављена на другу вредност по избору кодера).

Затварања

уреди

Затварање је потпрограм заједно са вредностима неке од својих варијабли из средине у којој је настала. Затварање су значајна карактеристика Lisp програмског језика, коју је увео Џон Макарти. У зависности од примене, затварачи могу послужити као механизам за споредне ефекте.

Конвенције

уреди

Развијен је велики број конвенција за кодирање потпрограма. Поред њиховог именовању, многи програмери су усвојили приступ да назив потпрограма треба да буде глагол када обавља одређени задатак, придев када обавља неку истрагу, и именицу када се користи да замени варијабле.

Неки програмери сугеришу да би потпрограм требало да обавља само један задатак, а ако потпрограм не обавља више од једног задатка, треба да буде подељен на више потпрограма. Они тврде да су потпрограми кључне компоненте у одржавању кода, и њихове улоге у програму морају остати различите.

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

Повратни кодови

уреди

Поред свог главног или нормалног ефекта, потпрограм можда морати да обавести позивни програм о посебним условима који су можда дошли током његовог извршења. У неким језицима и програмским стандардима, то се често врши путем повратног код, целобројне вредност постављена од стране потпрограма на неком стандардном месту, који кодира нормалне и посебне услове.

У IBM System/360, где се очекује повратни код из потпрограма, повратна вредност је често дизајнирана да буде дељива са 4-тако да може да се користи као директна филијала индекса табеле у грану која се често налази одмах након позивних инструкција да се избегну додатни условни тестови, што додатно побољшава ефикасност. У  System/360 assembly језику писало би на пример:

           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  }

Оптимизација потпрограмских позива

уреди

Постоји значајна дужина трајања преоптерећености у позиву потпрограма, укључујући и преношење аргумената, гранање на потпрограме, и гранање назад у саговорника. Преоптерећеност често укључује чување и обнављање одређених регистара процесора, издвајање и повратак меморије позивног рама, итд .. У неким језицима, сваки потпрограмски позив подразумева аутоматско тестирање повратка кода потпрограма, или руковање изузецима који могу да се појаве. У објектно-оријентисаним језицима, значајан извор преоптерећења је интензивно коришћен динамички обавештења за методске позиве.

Постоје неке наизглед очигледне оптимизације поступних позива које се не могу применити ако поступак може имати нежељене ефекте. На пример, у експресионом (f(x)-1)/(f(x)+1), функција f мора бити позвана два пута, јер два позива могу вратити различите резултате. Поред тога, вредност к мора бити поново преузета пре другог позива, будући да је први позив можда променио. Одређивање да ли потпрограм може имати нежељени ефекат је веома тешко (заправо, неодлучно). Дакле, док су ове оптимизације безбедне у чисто функционалним програмским језицима, компајлери типичног императивног програмирања обично морају да претпоставе најгоре.

Поравњање

уреди

Метод који се користи за елиминисање преоптерећности је поравњано проширење или поравњање тела потпрограма на сваком месту позива (у односу на гранање на потпрограм и назад). Не само да се овим избегава преоптерећење позива, него и омогућава компајлеру да ефикасније оптимизује поступно тело узимајући у обзир контекст и аргументе на том позиву. Убачено тело може бити оптимизовано од стране компајлера. Поравњање међутим, обично повећава величину кода, уколико програм садржи само један позив потпрограму, или је потпрограмско тело мањи код него преоптерећен позив .

Види још

уреди

Референце

уреди
  1. ^ U.S. Election Assistance Commission (2007). „Definitions of Words with Special Meanings”. Voluntary Voting System Guidelines. Архивирано из оригинала 08. 12. 2012. г. Приступљено 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. стр. 235. doi:10.1145/609784.609816. Архивирано из оригинала 28. 06. 2015. г. Приступљено 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. Приступљено 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. Приступљено 29. 09. 2013. 
  9. ^ „Overview of x64 Calling Conventions”. Msdn.microsoft.com. Архивирано из оригинала 03. 12. 2013. г. Приступљено 29. 09. 2013. 
  10. ^ „Function Types”. Msdn.microsoft.com. Приступљено 29. 09. 2013.