Пређи на садржај

Бесконачна петља

С Википедије, слободне енциклопедије

Бесконачна петља (или петља без граница) је низ упутстава у рачунарском програму који улазе у бесконачну петљу, било због петље која нема завршно стање, тј. завршетак петље је зависан од стања које се не може остварити, или критеријум завршетка петље изазива њен поновни почетак. У старијим оперативним системима са кооперативним мултитасковањем, бесконачне петље обично узрокују да цео систем престане да реагује. Са садашњим преовлађујућим превентивним мултитаскинг моделом, бесконачне петље узрокују да програм конзумира све доступно време процесора, али га корисник обично може зауставити. Петље активног чекања се такође понекад називају "бесконачним петљама". Један могући узрок "замрзавања" рачунара је бесконачна петља; остали укључују млаћење (енгл.thrashing), застој и грешка сегментације.

Планирано против непланираног петљања

[уреди | уреди извор]

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

Намерно петљање

[уреди | уреди извор]

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

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

Насупрот томе, модерни интерактивни рачунари захтевају да рачунар стално надгледа корисничке уносе или активност уређаја, тако да на неком основном нивоу постоји бесконачан процес idle петље која мора да настави све док се уређај не искључи или ресетује. У Apollo Guidance Рачунару, на пример, ова спољна петља се налазила у Exec програму, и ако рачунар не би имао никаквог посла, петља би се стартовала како би радила лажни посао који би искључио  светлосни показивач"рачунарске активности".

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

Ненамерно петљање

[уреди | уреди извор]

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

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

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

Све док систем реагује, бесконачне петље често могу бити прекинуте слањем сигнала у процесу (као што је SIGINT у Unix), или прекид у процесору, што узрокује да тренутни процес буде прекинут. Ово се може урадити у менаџеру задатака, у терминалу са командом Control-C , или користећи  "kill" команду или позив система. Међутим, ово не ради увек, јер процес можда не реагује на сигнала или процесор може бити у непрекидном стању, као у "Cyrix coma bug "(проузрокован преклапањем непрекидним инструкцијама у инструкционом "цевоводу"). У неким случајевима остали сигнали као што је SIGKILL може радити, пошто не захтевају процес да се оглашава, док у другим случајевима петља не може бити угашена гашењем система.

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

[уреди | уреди извор]

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

Неки језици имају посебне конструкције за бесконачне петље, типично изостављајући стање из бесконачне петље. Примери укључују Ada (loop... end loop),[1] Fortran (DO... END DO), Go (for {... }), и Ruby (loop do... end).

Примери намерних бесконачних петљи

[уреди | уреди извор]

Најједноставнији примери (у C):

int main()
{
  for (;;); // or while (1);
}

Образац for (;;) за бесконачну петљу је традиционалан, који се појављује у стандарној референци  С Програмски језик, and и често се изговара "заувек".[2]

Ово је петља која ће штампати "Бесконачна Петља" без заустављања.

Једноставан пример у Бејсик-у :

10 PRINT "INFINITE LOOP"
20 GOTO 10

Једноставан пример у Аемблер x86:

loop:
 ; Code to loop here
  jmp loop

Остали примери у DOS-у

:A
goto :A

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

while True:
    print("Infinite Loop")

Пример у Баш-у

 $ while true; do echo "Infinite Loop"; done

Пример у Перл-у

print "Infinite Loop\n" while 1

Примери ненамерних бесконачних петљи

[уреди | уреди извор]

Математичке грешке

[уреди | уреди извор]

Овде је пример бесконачне петље у Вижуал бејсику:

dim x as integer
do while x < 5
  x = 1
  x = x + 1
loop

Ово ствара ситуацију где x никада неће бити веће од 5, пошто је на почетку петље променљивој x дата вредност 1, стога ће се итерација увек завршити са 2 и петља никад неће престати. То се може поправити премештањем x = 1 инструкције изван петље. Есенцијално ова петља налаже рачунару да настави с додавањем 1 на 1 док се не досегне вредност 5. Пошто је 1+1 увек једнако 2, до тога никад не долази. У неким језицима, збуњеност програмера због математичких симбола може довести до ненамерне бесконачне петље. На пример, овде је одломак у С:

#include <stdio.h>

int main(void)
{
   int a = 0;
   while (a < 10) {
      printf("%d\n", a);
      if (a = 5)
         printf("a једнако 5!\n");
      a++;
   }
   return 0;
}

Очекивани излаз су бројеви од 0 до 9, са прекидом "a једнако 5!" између 5 и 6. Међутим, у реду "if (a = 5)" изнад, програмер је помешао = (доделу) са == (тестом једнакости) оператора. Уместо тога, ово ће доделити вредност 5 a  у овом делу програма. Стога, a никада неће бити у могућности да напредује до 10, и ово петља се не може прекинути.

Руковање над грешкама са променљивом

[уреди | уреди извор]

Неочекивано понашање у процени завршног стања може такође проузроковати проблем. Ево примера (у C):

float x = 0.1;
while (x != 1.1) {
  printf("x = %f\n", x);
  x = x + 0.1;
}

На неким системима, ова петља ће се извршити десет пута као што је и очекивано, али се на осталим системима неће извршити. Проблем је у томе што петља окончава услов (x != 1.1) теста за тачне једнакости две вредности, и начин по ком су вредности заступљене у многим рачунарима ће учинити да тест не успе, јер се не може представити тачна вредност 1.1. Исто се може десити и у  Пајтону:

x = 0.1
while x != 1:
    print x
    x += 0.1

Због вероватноће тестова за  једнакост или неједнакост који су неочекивано неисправни, сигурније је користити више-од или мање-од тестове када се ради о  "floating-point "вредностима. На пример, уместо да се тестира да ли је  x једнако 1.1, могло би се проверити да ли  је (x <= 1.0), или (x < 1.1), који би сигурно изашли накод одређеног броја понављањае. Још један начин да се поправи овај пример био би уз коришћење целог броја као индекс петље, бројећи број понављања који су били извршени.

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

Вишепартијска петља

[уреди | уреди извор]

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

Квази-бесконачне петље

[уреди | уреди извор]

Квази-бесконачна петља је петља која се појављује у бесконачности, али је само веома дуга петља.

Немогућ престанак услова

[уреди | уреди извор]

Пример for петље у C:

unsigned int i;
for (i = 1; i != 0; i++) {
  /* loop code */
}

Чини се да ће ово ићи унедоглед, али уставари ће вредност i на крају достићи максималну складишну вредност у unsigned int и додавање броја 1 том броју ће се завршити са 0, окончавајући петљу. Стварна граница i зависи од система и компилатора који се користи. Са произвољно прецизном рачуницом, ова петља ће наставити са радом све док рачунарска меморија не може више садржати i. Ако је i припицан цео број, више него не приписан цео број, преливање неће бити дефинисано. У том случају, петља може бити оптимизована у бесконачну петљу.

Бесконачна рекурзија

[уреди | уреди извор]

Бесконачна рекурзија је специјалан случај бесконачне петље која је проузрокована рекурзијом . Најтривијалнији пример овог услова Ω у ламбда рачуну , показан је доле у Scheme:

(define Ω
  (let ([ω (lambda (f) (f f))])
    (ω ω)))

Ω је бесконачна рекурзија, стога нема нормалну форму. Када се користи структурна рекурзија, бесконачне рекузије су обично изазване недостатком главног случаја или неисправним индуктивним кораком. Пример погрешне структурне рекурзије:

(define (sum-from-1-to n)
  (+ n (sum-from-1-to (sub1 n))))

Функција sum-from-1-to ће остати без простора, пошто рекурзија никада не престаје — бесконачна је. Како би се исправио проблем, додат је главни случај.

(define (sum-from-1-to' n)
  (cond
    [(= n 1) 1]
    [else (+ n (sum-from-1-to' (sub1 n)))]))

Овој измењеној функцији ће нестати простора само ако је n мање од 1 или ако је n превелико; проналазач грешке би избрисао први случај. За информације о рекурзивним функцијама које никада неће остати без простора, погледати "реп" рекурзију.

Види још: Рекурзија, за додатно објашњење бесконачне рекурзије.

Изјава прекид

[уреди | уреди извор]

 "while (true)" петља на први поглед изгледа као бесконачна, али можда постоји начин да се избегне петља кроз прекид изјаву или кроз изјаву повратка.

На пример у PHP:

while (true) {
   if ($foo->bar()) {
      return;
   }
}

Алдерсонова петља

[уреди | уреди извор]

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

Као у C- псеудокоду пример Алдерсонове петље, где се од програма очекује да сумира бројеве дате од стране корисника док нула није дата, али где је програмер користио погрешног оператора:

sum = 0;
while (true) {
   printf("Убацити број како би се додало суми или 0 за излаз");
   i = getUserInput();
   if (i * 0) { // ако је i пута 0 тачно, додати i суми
      sum += i; // сума се не мења зато што је (i * 0)  0 за било које i; променило би ако бисмо имали != у услову уместо *
   }
   if (sum > 100) {
      break;    // зауставља петљу; стање излаза постоји али никад се не долази до њега зато што никада сума није додата
   }
}

Термин је наводно добио име од програмера који је кодирао модалну поруку кутије у Мајкрософт аксес без ОК или Cancel дугмета, чиме се онемогућава читав програм сваки пут када се кутија појави.[3]

Референце

[уреди | уреди извор]