Управљање током

Из Википедије, слободне енциклопедије
(преусмерено са Контрола тока)
Иди на навигацију Иди на претрагу

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

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

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

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

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

Прекиди и сигнали су механизми ниског нивоа који мењају ток управљања на сличан начин као и потпрограми, али се пре јављају као одговор на неке екстерне стимулусе (који могу да се десе асинхроно), него на „ин-лајн наредбе управљања током.

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

Садржај

Примитиве[уреди]

Лабеле[уреди]

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

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

10 LET X = 3
20 PRINT X

У другим језицима као што су C и Ада, лабела је идентификатор који се обично појављује на почетку реда, праћен колоном. На пример, у програмском језику C:

Success: printf("The operation was successful.\n");

Програмски језик Алгол 60 је дозвољавао како целе бројеве тако и идентификаторе као лабеле (оба закачена за колону са одређеном наредбом), али само неколико у случају да је нека друга варијанта Алгола дозвољавала целе бројеве.

Goto[уреди]

goto наредба (реч настала спајањем енглеских речи гоу и ту) је најједноставнија форма безусловног преноса управљања.

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

   goto label

Циљ гоуту наредбе је да изазове извршавање наредбе која је дата одређеном лабелом (или следи одмах након ње).

Гоуту наредбе се сматрају штетним од стране многих стручњака из области рачунарства, међу којима је и Едсгер Дајкстра.

Потпрограми[уреди]

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

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

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

Минимално структурирано управљање током[уреди]

У мају 1966., Бом и Јакопини су објавили чланак у часопису Комуникације АЦМ-а у којем тврде да било који програм са гоутујем може да се преобрази у програм без гоутуа користећи само избор (IF THEN ELSE) и петље (WHILE услов DO xxx), могуће са дуплираним кодом и/или додавањем Булових варијабли (тачно/нетачно). Касније су аутори доказали да избор може бити замењен петљама (и још више Булових променљивих).

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

Бомов и Јакопинијев чланак је показао да сви програми могу бити без гоуту-а. Остала истраживања су показала да су контролне структуре са по једним улазом и излазом биле много лакше за разумевање него било која друга форма, највише због тога што су могле да се користе свуда као наредбе, без ремећења управљања током. Другим речима, биле су композабилне. (Каснија унапређења, као што су не-стриктни програмски језици - или ранија, композабилне софтверске трансакције - су пратиле овај правац мишљења, што је чинило компоненте програма још више композабилним.)

Неки академици су пуристички приступили резултатима Бома и Јакопинија и започели расправу о томе да су чак и break и return наредбе које се налазе у петљама лоше у пракси пошто уопште нису потребне у наведеном доказу, стога су се залагали да све петље треба да имају само један излаз. Овакав пуристички приступ је основа програмског језика Паскал (дизајнираног 1968-1969), који је до средине деведесетих година двадесетог века био префериран језик за програмере почетнике на факултетима.[1] Директна примена Бом-Јакопини теореме може довести до појаве додатних локалних променљивих у структурираном графикону, и такође довести до понављања у коду.[2] Потоњи проблем се назива петља и по у овом контексту.[3] Паскал је захваћен са оба наведена проблема и судећи по емпиријским истраживањима Ерика С. Робертса, студенти су имали потешкоћа са формулацијом тачних решења за неколицину простих проблема, укључујући писање функције која тражи елемент у низу. У истраживању Хенрија Шапира из 1980. наведеног од стране Робертса, стоји податак да користећи само контролне структуре које нуди Паскал, само 20% субјеката је дало тачан одговор, док ниједан субјекат није написао погрешан код за решење проблема ако му је било дозвољено да напише return наредбу у телу петље.[1]

Контролне структуре у пракси[уреди]

Већина програмских језика који садрже контролне структуре поседују и иницијалну кључну реч која казује који тип контролне структуре је садржан. Језици се тада деле на оне чије контролне структуре имају односно немају финалну кључну реч.

  • Без финалне кључне речи: Алгол 60, C, C++, Хаскел, Јава, Паскал, Перл, PHP, ПЛ/И, Пајтон, ПауерШел. Оваквим језицима је потребан неки начин груписања изјава:
    • Алгол 60 и Паскал : begin ... end
    • C, C++, Јава, Перл, PHP и ПауерШел: витичасте заграде { ... }
    • ПЛ/1: DO ... END
    • Пајтон: користи ниво увлачења (види правило са-стране)
    • Хаскел: могу се користити и метод увлачења и витичасте заграде, уз слободу да се мешају
  • Финалне кључне речи: Ада, Алгол 68, Модула-2, Фортран 77, МитрилВижуал бејсик. Форме финалних кључних речи:
    • Ада: Финална кључна реч је end + спејс + иницијална кључна реч нпр. if ... end if, loop ... end loop
    • Алгол 68, Митрил: иницијална кључна реч писана отпозади нпр. if ... fi, case ... esac
    • Фортран 77: финална кључна реч је end + иницијална кључна реч нпр. IF ... ENDIF, DO ... ENDDO
    • Модула-2: иста финална кључна реч END за све
    • Вижуал бејсик: свака контролна структура има своју кључну реч. If ... End If; For ... Next; Do ... Loop; While ... Wend

Избор[уреди]

If-then-(else) наредбе[уреди]

Условни изрази и условне конструкције су одлике програмског језика који извршава различите операције у зависности да ли Булов услов враћа true или false.

  • IF..GOTO. Форма која се среће у неструктурираним програмским језицима која имитира обичну инструкцију машинског кода, би требало да скочи (GOTO) до ознаке или линијског броја када се услов испуни.
  • IF..THEN..(ENDIF). Уместо да буду ограничене на скок, било која проста наредба или угнежђени би могли да прате THEN кључну реч. Ово је структурирана форма.
  • IF..THEN..ELSE..(ENDIF). Као горенаведено, али предузимајући другу радњу уколико је услов false. Ово је једна од најзаступљенијих форми, са много варијација. Неке захтевају завршно ENDIF, неке не. C и слични језици не захтевају завршну кључну реч, или 'then', али захтевају заграде око услова.
  • Условне наредбе су често угнежђене унутар условних наредби. Неки језици дозвољавају ELSE и IF да се комбинују у ELSEIF, избегавајући потребуза већим бројем ENDIF или осталих финалних наредби на крају сложене наредбе.

Мање познате варијације укључују:-

  • Неки језици, као што је Фортран, имају "троструко" или "аритметичко if", тестирање, које проверава да ли је нумеричка вредност позитивна, негативна или нула..
  • Неки језици поседују операторску форму "if" наредбе, као на пример тројни оператор у језику C.
  • Перл пружа if са when и unless по узуру на C.
  • Smalltalk користи ifTrue и ifFalse поруке за имплементацију кондиционала, радије него неке основне језичке конструкције.

Наредбе замене и предмета[уреди]

Наредбе замене (или наредбе предмета, или вишеструке гране) пореде задату вредност са одређеним константама и започињу извршавање у зависности од прве константе са којом се поклопе. Обично постоји ограничен број уобичајених акција ("else", "otherwise") које се предузимају ако се ниједно поклапање не догоди. Наредбе замене дозвољавају оптимизацију компајлера, као што су лукап табеле. У динамичким језицима, предмети не морају бити ограничени константним изразима, и могуће је да се продуже до поклапања шаблона, као у примеру шел скрипте са десне стране, где *) имплементира уобичајени случај као глоб који се поклапа са било којим стрингом. Логика случаја се такође може имплементирати у функционалну форму, као у SQL-овој decode наредби.

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))

Петље[уреди]

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

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

Петље контролисане бројањем[уреди]

Већина програмских језика поседује конструкције за понављање петље одређен број пута. Треба запазити да, ако је N мање од 1 у овим случајевима, онда језик заправо спецификује да тело треба потпуно прескочити, или извршити једанпут ако је N=1. У већини случајева, бројање је могуће и уназад, а не мора ни бити узастопно.

   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                   | }

У великом броју програмских језика, само се интиџери могу поуздано користити за петљу која је контролисана бројачем. Флоутинг-поинт бројеви нису прецизно представљени због ограничења хардвера, тако да петља нпр.:

   for X := 0.1 step 0.1 to 1.0 do

можда може бити поновљена 9 или 10 пута, у зависности од грешака са саокруживањем бројева и/или хардвера и/или верзије компајлера. Осим тога, ако се повећање Х-а догоди пуетм поновљењог сабирања, нагомилане грешке у заокруживању значити да се вредност Х у свакој итерацији може доста разликовати од очекиваног низа 0.1, 0.2, 0.3, ..., 1-0.

Условно-контролисане петље[уреди]

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

   DO WHILE (test)          | repeat 
       xxx                  |     xxx 
   LOOP                     | until test;
----------------------------------------------
   while (test) {           | do
       xxx                  |     xxx
   }                        | while (test);

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

   DO UNTIL (End-of-File)
      IF new-zipcode <> current-zipcode
         display_tally(current-zipcode, zipcount)
         
         current-zipcode = new-zipcode
         zipcount = 0
      ENDIF
      
      zipcount++
   LOOP

Петље контролисане колекцијом[уреди]

Неколицина програмских језика (нпр. Ада, D, Smalltalk, PHP, Перл, Објектни Паскал, Јава, C#, Матлаб, Митрил, Вижуал Бејсик, Руби, Пајтон, Јаваскрипт, Фортран 95 и каснији) поседују специјалне структуре које дозвољавају понављање петље кроз све елементе низа, или све чланове комплета или колекције.

   некаКолекција do: [:свакиЕлемент |xxx].
   for Предмет in Колекција do begin xxx end;

   foreach (предмет; мојаКолекција) { xxx }

   foreach некиНиз { xxx }

   foreach ($некиНиз as $k => $v) { xxx }

   Колекција<Ниска> coll; for (Ниска s : coll) {}

   foreach (ниска s in мојаНискаКолекција) { xxx }

   $некаКолекција | ЗаСваки-Објекат { $_ }
   forall ( index = first:last:step... )

Скала има for-наредбе, које генерализују петље контролисане колекцијама, и помажу друге радње, као што је паралелна обрада. Хаскел има do-наредбе, које пружају сличну функционалност као и for-израза у Скали.

Општа итерација[уреди]

Структуре генералне итерације, као што је for-наредба у C-у и do форма у Common Lisp-у, се могу искористити за изражавање било које од горе наведених петљи, као и осталих—нпр. петљање преко низа збирки паралелно. Тамо где се може применити конкретнија структура петље, она је приоритетнија од обичне итеративне структуре, пошто чини сврху израза јаснијом.

Бесконачне петље[уреди]

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

Бесконачне петље могу бити реализоване коришћењем других конструкција за контролу тока. Најчешће, код неструктурираног програмирања ово је скок уназад (goto), док је у структурираном програмирању ово бесконачна петља (while петља) подешена да се никада не заврши, тако што се услов изостави или једноставно постављање истог услова на True, као while (true) .... Неки језици поседују специјалне конструкције за бесконачне петље, обично је то изостављање услова из бесконачне петље. Неки од поменутих језика су Ада (loop ... end loop),[4] Фотран (DO ... END DO), Гоу (for { ... }), и Руби (loop do ... end).

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

Континуација са наредном итерацијом[уреди]

Понекад у телу петље постоји потреба да се прескочи остатак петље и да се настави са следећом итерацијом петље. У неким језицима постоји наредба као што је continue (већина језика), skip, или next (Перл и Руби), која ће то учинити. Циљ је да се елиминише извршење тела петље која се налази у средини кода и да се настави са нормалним извршавањем следеће итерације. Ако је итерација на последњем месту у петљи, циљ је да се рано елиминише читава петља.

Понављање тренутне итерације[уреди]

Неки језици, попут Перла и Рубија, поседују redo наредбу која започиње тренутну итерацију од почетка.

Поновно покретање петље[уреди]

Руби поседује retry наредбу која покреће целу петљу испочетка, од прве итерације.

Рани излазак из петље[уреди]

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

Наредни пример је приказан у језику Ада који подржава и рани излаз из петље и петље са провером у средини. Обе одлике су веома сличне и само поређењем исечака из оба кода може се видети разлика: рани излаз захтева да стоји у комбинацији са if наредбом, док је провера у средини самостална конструкција.

with Ada.Text IO;
with Ada.Integer Text IO;

procedure Штампај_Квадрате 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 Штампај_Квадрате;

Пајтон подржава условно извршавање кода у зависности од тога да ли је се рано изашло из петље (са break наредбом), или није - користећи се else-клаузулом са петљом. На пример,

for n in низ_бројева:
    if прост_број(n):
        print "Низ садржи прост број"
        break
else:
    print "Низ не садржи прост број"

Треба запазити да је else клаузула у горенаведеном примеру закачена за for наредбу, а не унутрашњу if наредбу. Обе петље у Пајтону,  for и while , подржавају else клаузулу, која се извршава само у случају да се рани излазак из петље није догодио.

Неки језици подржавају прекидање из угнежђених петљи; у теоријским круговима, то се зове избијање са више нивоа. Типична примена овог принципа је претраживање вишедимензионалних табела. Ово се може урадити помоћу прекидања са више нивоа (прекидање са N нивоа), као у bash-u[5] и PHP-у,[6] или путем означених прекида (избиј и настави код дате ознаке), као у Јави и Перлу.[7] Алтернативе за прекиде са више нивоа укључују једноструке прекиде, заједно са нередбеним променљивама који се користе за прекид још једном нивоу; изузеци, који се налазе на нивоу на који се прекида; стављање угнежђене петље у функцију и коришћење return наредбе за изазивање прекида читаве угнежђене петље; или коришћење ознаке и goto исказа. C не подржава прекид са више нивоа, и уобичајена алтернатива је коришћење goto наредбе за имплементацију означеног прекида.[8] Пајтон не поседује прекид са више нивоа нити наставак – ово је предложено у PEP 3136, али је одбачено на основу тога што додатна комплексност није била вредна легитимне.[9]

Појам прекида са више нивоа неким делом битна за теоријско рачунарство, јер доводи до нечега што се данас назива the ,, косараџу хијерархија''[10] Године 1973. С. Рао Косараџу је прерадио теорему структурираних програма тако што је доказао да је могуће да се избегне додавање додатних променљивих у структурираном програмирању, док код су произвољно-дубоки прекиди са више нивоа дозвољени у петљи.[11] Штавише, Косараџу је доказао да постоји строга хијерархија програма: за сваки интеџер n постоји програм који садржи прекид са више нивоа дубине n који се не може прерадити као програм са прекидима са више нивоа дубине мање од n без увођења додатних променљивих.[10]

Такође се може користити return наредба у потпрограму која извршава наредбе у петљи, вршећи тиме прекид како угнежђене петље, тако и потпрограма. There are other proposed control structures for multiple breaks, but these are generally implemented as exceptions instead.

У свом уџбенику из 2004., Дејвид Ват користи Тенентов појам of секвенцера за објашњење сличности прекида са више нивоа са return наредбама. Ват напомиње да класа секвенцера познатих као излазни секвенцери, дефинисаних као "секвенцер који прекида извршавање команде или процедуре која прима текст", обухвата како прекиде петљи (укључујући прекиде са више нивоа) и return наредбе. Међутим, иако се често имплементира, return секвенцери такође могу носити (return) вредност, док the break секвенцер имплементиран у савременим језицима обично не може.[2]

Варијанте и инваријанте петљи[уреди]

Варијанте петљи и инваријанте петљи се користе за изражавање исправности петљи.[12]

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

Инваријанта петље је тврдња која мора бити тачна пре прве итерације у петљи и остати тачна после сваке итерације. Ово подразумева да када се петљање исправно оконча, и излазни услов и инваријанта петље морају бити задовољени. Инваријанте петље се користе за праћење одређених својстава петље током узастопних итерација.

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

Подјезик петље[уреди]

Неки Lisp дијалекти пружају богат подјезик за описивање петљи. Рани пример се може наћи у Конверзионалном Lisp-у Интерлиспа. Common Lisp  пружа макро за петље који имплементира такав подјезик.[13]

Табела система који управљају петљама[уреди]

Програмски језик услов петља рани излаз континуација поновни рад поновни покушај тестирање исправности
почетни средишњи крајњи преброј колекција општа бесконачна [1] варијанта инваријанта
Ada Да Да Да Да низови Не Да дубоко угнежђено Не
C Да Не Да Не [2] Не Да Не дубоко угнежђено [3] дубоко угнежђено [3] Не
C++ Да Не Да Не [2] Да [9] Да Не дубоко угнежђено [3] дубоко угнежђено [3] Не
C# Да Не Да Не [2] Да Да Не дубоко угнежђено [3] дубоко угнежђено [3]
КОБОЛ Да Не Да Да Не Да Не дубоко угнежђено [14] дубоко угнежђено [14] Не
Common Lisp Да Да Да Да builtin only [16] Да Да дубоко угнежђено Не
D Да Не Да Да Да Да Да[14] дубоко угнежђено дубоко угнежђено Не
Eiffel Да Не Не Да [10] Да Да Не један ниво [10] Не Не Не [11] само цели бројеви [13] Да
F# Да Не Не Да Да Не Не Не [6] Не Не
ФОРТРАН 77 Да Не Не Да Не Не Не један ниво Да
Фортран 90 Да Не Не Да Не Не Да дубоко угнежђено Да
Фортран 95 and later Да Не Не Да низови Не Да дубоко угнежђено Да
Haskell Не Не Не Не Да Не Да Не [6] Не Не
Java Да Не Да Не [2] Да Да Не дубоко угнежђено дубоко угнежђено Не није уграђен [12] није уграђен [12]
ЈаваСкрипт Да Не Да Не [2] Да Да Не дубоко угнежђено дубоко угнежђено Не
[ОКамл]] Да Не Не Да низови, листе Не Не Не [6] Не Не
PHP Да Не Да Не [2] [5] Да [4] Да Не дубоко угнежђено дубоко угнежђено Не
Перл Да Не Да Не [2] [5] Да Да Не дубоко угнежђено дубоко угнежђено Да
Python Да Не Не Не [5] Да Не Не дубоко угнежђено [6] дубоко угнежђено [6] Не
РЕБОЛ Не [7] Да Да Да Да Не [8] Да један ниво [6] Не Не
Ruby Да Не Да Да Да Не Да дубоко угнежђено [6] дубоко угнежђено [6] Да Да
Стандард МЛ Да Не Не Не низови, листе Не Не Не [6] Не Не
Вижуал Бејсик. НЕТ Да Не Да Да Да Не Да један ниво по нивоу петље један ниво по нивоу петље
Виндоус ПауерШел Да Не Да Не [2] Да Да Не ? Да
  1. a while (true) се не рачуна као бесконачна петља у ову сврху, јер није права језичка структура.
  2. a b c d e f g h C-ова for (init; test; increment) петља је генерална конструкција петље, али не нужно бројећа, иако је често коришћена у ту сврху.
  3. a b c Дубоки прекиди се могу остварити у језицима C, C++ и C# коришћењем ознака и goto-а.
  4. a Итерација над објектима је додата у PHP 5.
  5. a b c Петља са бројањем се може имитирати итерацијом над an листом која се повећава или генератором, на пример Пајтонова range() наредба.
  6. a b c d e Дубоки прекиди се могу остварити помоћу управљања изузецима.
  7. a Не постоји специјална конструкција, пошто се while функција може користити у ову сврху.
  8. a Не постоји специјална конструкција, али корисници могу дефинисати обичне функције петље.
  9. a Нацрт C++11 је увео for функцију на основу низа. У STL постоји std::for_each шаблон који може вршити итерације над СТЛ колекцијама и позвати унарну функцију за сваки елемент.[14] Функционалност се такође може конструисати као macro над овим колекцијама.[15]
  10. a Петљање контролисано бројањем је под утицајем итерације кроз интервал целих бројева; рани излаз из петље укључујући додатни услов за излаз.
  11. a Ајфел подржава резервисану реч retry, али се она користи у управљању изузецима, а не контроли петље.
  12. a Захтева Јава Језик за Моделовање (JML).
  13. a Захтева да варијанте петље буду цели бројеви; трансфинит варијанте нису подржане. [1]
  14. a D подржава бесконачне колекције, и могућност итерације над тим колекцијама. Ово не захтева никакву специјалну конструкцију.
  15. a Дубоки прекиди се могу остварити коришћењем GO TO и процедура.
  16. a Common Lisp претходи концепту генеричког типа колекција.

Структурирано ванлокално управљање током[уреди]

Многи програмски језици, посебно они у којима се практикује динамичан стил програмирања, нуде конструкције за ванлокално управљање током. Оне доводе до тога да ток извршавања искочи из датог контекста и настави се у некој одређеној тачки. Услови, изузеци и континуације су три опште врсте ванлокалних контролних конструкција; постоје и комплексније врсте, као што су генератори, копрограми и кључна реч async.

Услови[уреди]

ПЛ/И поседује 22 стандардна услова (нпр. ZERODIVIDE SUBSCRIPTRANGE ENDFILE) који се могу подићи и пресрести путем: ON условном акцијом; Програмери такође могу дефинисати и користити своје именоване услове.

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

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

Прости примери синтаксе:

 ON услов GOTO ознака

Изузеци[уреди]

Савремени језици have поседују специјализоване структуриране конструкције за управљање изузецима које се не ослања на коришћење наредбе GOTO или break (са више нивоа) или return наредбама. На пример, у језику C++ може се написати:

try {
    xxx1                                  // Негде унутар овога
    xxx2                                  //     use: '''throw''' someValue;
    xxx3
} catch (некаКласа& someId) { // ухвати вредност од некаКласа
    actionForSomeClass 
} catch (некиТип& anotherId) { // ухвати вредност од некиТип
    actionForSomeType
} catch (...) { // ухвати било шта што још није ухваћено
    actionForAnythingElse
}

Било који број и варијетет catch клаузула се може користити у горенаведеном примеру. Ако не постоји catch који се подудара са одређеним throw, контрола се враћа назад кроз позиве потпрограма и/или угнежђене блокове док се не пронађе catch који се поклапа или док се не достигне крај главног програма, у том случају се програм насилно гаси са одговарајућим обавештењем о грешци. Захваљујући утицају језика C++, catch је кључна реч резервисана за декларацију управљача изузетака који ради на принципу препознавања шаблона у осталим, данас популарним, језицима, као што су Јава или C#. Неки други језици као што је Ада користе кључну реч exception за увођење управљача изузецима и можда употребе другу кључну реч (when у језику Ада) за поклапање шаблона. Неколико језика попут ЕплСкрипта уводе плејсхолдере у синтаксу управљача изузецима за аутоматско вађење неколико делова информација када се изузетак догоди. Овај приступ је дат у примеру испод косритећи on error конструкцију у ЕплСкрипту:

try
    set мојБрој to мојБрој / 0
on error e number n from f to t partial result pr
    if ( e = "Немогуће делити нулом" ) then display dialog "Не смете то радити"
end try

У уџбенику Давида Вата из 2004 године се такође анализира управљање изузецима у оквиру секвенцера (о овоме више пише у овом чланку, у одељку о раним излазима из петљи.) Ват истиче да абнормална ситуација, као на пример аритметичко преливање или input/output квара као што је ,, датотека није пронађена“, је попут грешке која "се детектује у некој јединици програма на ниском нивоу, али [за коју] се управљач у већини случајева налази у програмској јединици високог нивоа". На пример, програм може да садржи више позива за читање датотека, али радња која треба да се изврши у случају да датотека није пронађена зависи од значења (сврхе) те датотеке за програм и зато се рутина за управљање овом абнормалном ситуацијом не може наћи у системском коду ниског нивоа. Касније Ват наводи да увођење тестирања статусних ознака у позиваоцу, како би се то захтевало у једноизлазном структурираном програмирању или чак (вишеизлазном) повратном секвенцеру, резултује у томе да "апликацијски код обично бива претрпан тестовима статусних ознака" и да "програмер можда не тестира статусну ознаку као последица лењости или заборавности. У ствари, абнормалне ситуације представљене од стране статусних ознака су по правилу игнорисане!" Ват наглашава да у супротности са тестирањем статусних ознака, изузеци имају супротно опште понашање, што доводи до гашења програма осим у случају да се програмер не позабави са изузетком на неки начин, нпр. додајући у код наредбе за игнорисање. На основу ових аргумената, Ват доноси закључак да jump секвенцери или escape секвенцери нису погодни колико и секвенцер изузетака са доленаведеном семантиком.[2] У језицима Обџект Паскал, D, Јава, C#, и Пајтон, клаузула finally се може додати на try конструкцију. Без обзира на то како контрола напушта try, код унутар finally клаузуле се загарантовано извршава. Ово је корисно у случају писања кода који треба да се одрекне битних ресурса (као што је отворена датотека или веза са базом података) на крају израчунавања:

FileStream stm = null; // C# пример
try {
    stm = new FileStream ("logfile.txt", FileMode.Create);
    return ProcessStuff(stm);             // можда избаци изузетак
} finally {
    if (stm != null)
        stm.Close();
}

Пошто је овакав шаблон доста заступљен, C# има специјалну синтаксу:

using (FileStream stm = new FileStream ("logfile.txt", FileMode.Create)) {
    return ProcessStuff(stm);             // можда избаци изузетак
}

По напуштању using-блока, компилатор гарантује да је stm објекат ослобођен, спајајући тако променљиву за ток датотека док паралелно абстрахује од споредних ефеката иницијализације и ослобађања датотеке. Пајтонова with наредба и Рубијев блок аргумент за File.open се користе уз сличне исходе.

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

Корисници могу да избацују сопствене изузетке; C++ дозвољава корисницима да избаце и хватају било који тип, укључујући основне типове попут int, док неки други језици не дозвољавају такве радње.

Континуације[уреди]

Async[уреди]

C# 5.0 је увео async кључну реч као подршку асинхроном I/O у "директном стилу".

Генератори[уреди]

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

Копрограми[уреди]

Копрограми су функције које могу да међусобно предају контролу - облик ко-оперативног мултитаскинга.

Копрограми се могу имплементирати као библиотека у случају да програмски језик омогућава континуације или генераторе - тако да разлике између копрограма и генератора у пракси постају само технички детаљ.

Табела ван-локалног управљања током[уреди]

Програмски језик услови изузеци генератори/копрограми async
Ada Не Да ? ?
C Не Не Не Не
C++ Не Да да, помоћу BOOST-a ?
C# Не Да Да Да
Cobol Да Да Не Не
Common Lisp Да Не ? ?
D Не Да Да ?
Eiffel Не Да ? ?
Erlang Не Да Да ?
F# Не Да Да Да
Go Не Да Да ?
Haskell Не Да Да Не
Java Не Да Не Не
Објектни C Не Да Не ?
PHP Не Да Да ?
ПЛ/И Да Не Не Не
Python Не Да Да ?
РЕБОЛ Да Да Не ?
Ruby Не Да Да ?
Scala Не Да Не помоћу експерименталне екстензије
Тцл помоћу трагова Да Да помоћу петље догађаја
Вижуал Бејсик . НЕТ Да Да Не ?
Виндоус ПауерШел Не Да Не ?

Предложене контролне структуре[уреди]

У сатиричном чланку[16] листа Датамејшн из 1973., Р. Лоренс Кларк је предложио да се GOTO наредба може заменити COMEFROM наредбом, и навео неке занимљиве примере. Ово је заиста имплементрирано у језику INTERCAL, a наменски езотеричном програмском језику.

У свом чланку из 1974. "Структурирано Програмирање са go to Наредбама",[17] Доналд Кнут је препознао две ситуације које нису објашњене горенаведеним контролним структурама, и изнео примере контролних структура које би могле да управљају овим ситуацијама. Упркос њиховој корисности, ове конструкције и даље нису заступљене у конвенционалним програмским језицима.

Петља са средишњим тестирањем[уреди]

Предложено од стране  in 1972:[18]

   loop                           loop
       xxx1                           read(char);
   while test;                    while not atEndOfFile;
       xxx2                           write(char);
   repeat;                        repeat;

Ако се изостави xxx1, добијамо петљу са тестирањем на врху.

Ако се изостави xxx2, добијамо петљу са тестирањем на крају.

Ако се while изостави, добијамо бесконачну петљу.

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

Једна од могућих варијанти је да се дозволи тестирање while наредбе више пута; унутар петље, али употреба exitwhen (погледај следећи одељак) боље покрива овај случај.

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

while (true) {
    xxx1
    if (not test)
        break
    xxx2
}

У језику Ада, горенаведена конструкција петље (loop-while-repeat) се може представити коришћењем стандардне бесконачне петље (loop - end loop) која има exit when клаузулу у средини (не треба помешати са exitwhen наредбом из наредног одељка).

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;

Додавање имена петљи (као Read_Data у овом примеру) је опционално али дозвољава излажење из спољне петље која се налази у неколико угнежђених петљи.

Вишеструки рани излаз/излаз из угнежђених петљи[уреди]

Ово је предложио Цан1974.[19] Промењена верзија је приказана овде.

   exitwhen ДогађајA or ДогађајБ or ДогађајЦ;
       xxx
   exits
       ДогађајА: радњаA
       ДогађајБ: радњаБ
       ДогађајЦ: радњаЦ
   endexit;

exitwhen се користи за спецификацију догађаја који се могу одиграти унутар xxx, њихово догађање је доказано коришћењем имена догађаја као наредбе.

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

Ова конструкција пружа веома јасну разлику између одређивања да ли се нека ситуација примењује и радње која се треба извржити за ту ситуацију.

exitwhen је по концепту слично управљању изузетака, а у ову сврху се користе изузеци или сличне конструкције у доста језика.

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

   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;

Види још[уреди]

Референце[уреди]

  1. 1,0 1,1 Roberts, E. [1995] “Loop Exits and Structured Programming: Reopening the Debate,” ACM SIGCSE Bulletin, (27)1: 268–272.
  2. 2,0 2,1 2,2 David Anthony Watt; William Findlay (2004).
  3. ^ Kenneth C. Louden; Kenneth A. Lambert (2011).
  4. ^ Ada Programming: Control: Endless Loop
  5. ^ Advanced Bash Scripting Guide: 11.3.
  6. ^ PHP Manual: "break"
  7. ^ perldoc: last
  8. ^ comp.lang.c FAQ list · "Question 20.20b"
  9. ^ [Python-3000 Announcing PEP 3136], Guido van Rossum
  10. 10,0 10,1 Kozen, Dexter (2008).
  11. ^ KOSARAJU, S. RAO.
  12. ^ Meyer (1991)
  13. ^ "Common Lisp LOOP macro".
  14. ^ for_each. Sgi.com. Retrieved on 2010-11-09.
  15. ^ Chapter 1. Boost.Foreach. Boost-sandbox.sourceforge.net (2009-12-19). Retrieved on 2010-11-09.
  16. ^ We don't know where to GOTO if we don't know where we've COME FROM.
  17. ^ Knuth, Donald E. "Structured Programming with go to Statements" ACM Computing Surveys 6(4):261-301, December 1974.
  18. ^ Dahl & Dijkstra & Hoare, "Structured Programming" Academic Press, 1972.
  19. ^ Zahn, C. T. "A control statement for natural top-down structured programming" presented at Symposium on Programming Languages, Paris, 1974.

Спољашње везе[уреди]