Structura de control

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare

În toate paradigmele de programare imperative , structurile de control sunt constructe sintactice ale unui limbaj de programare a cărui semantică se referă la controlul fluxului de execuție al unui program , adică servesc la specificarea dacă , când , în ce ordine și de câte ori trebuie să fie executate instrucțiuni care alcătuiesc codul sursă pe baza specificațiilor proiectului software - ului care urmează fie creat.

Structuri de control fundamentale

Secvenţă

Secvența este structura fundamentală de control a oricărui limbaj imperativ, inclusiv a limbajelor mașină . Determină ordinea în care instrucțiunile din textul programului trebuie executate în timpul rulării . De regulă, nu are o expresie sintactică explicită: în schimb este dată de simpla succesiune de instrucțiuni; în acest caz, mașina abstractă a limbajului utilizat le execută în ordine, una după alta.

Mergi la

Împreună cu secvența, go to (go to) este cea mai simplă structură de control; apare și el, într-o anumită formă, în toate limbile mașinilor . Înțelesul general al lui goto este să „săriți” sau să „treceți” controlul către o instrucțiune specificată, care poate fi oriunde în program. Mergeți întotdeauna de asemenea (sau numai) admite o formă condițională , al cărei sens poate fi parafrazat după cum urmează: „dacă condiția C este adevărată, mergeți la enunțul I ”. În limbajele mașinii, condiția C trebuie să ia de obicei una dintre următoarele două forme: „conținutul celulei de memorie M este 0” sau „conținutul celulei de memorie M este diferit de 0”; instrucțiunea I este identificată și prin adresa de memorie în care este stocat I. În limbaje de nivel înalt, cum ar fi C , condiția C poate fi exprimată ca orice expresie booleană (deoarece rezultatul înmulțirii lui A cu B este diferit de X ), iar instrucțiunea I poate fi identificată printr-un nume sau printr-un cod numeric în mod explicit asociat de programator instrucțiunii în sine (indiferent de locația sa în memorie).

Începând cu anii șaptezeci , structura de control Goto a fost supus unor critici puternice , deoarece permite (sau favoruri) dezvoltarea unor programe care sunt potențial foarte greu de citit și modificabil (așa-numitul cod de spaghete ). Deși rămâne o structură fundamentală de control a limbajelor moderne ale mașinilor, în limbaje de programare la nivel înalt, după caz, goto nu este furnizat sau este furnizat, dar utilizarea acestuia nu este recomandată.

Structuri de control al programării structurate

În 1966 , cu o celebră teoremă , Corrado Böhm și Giuseppe Jacopini au introdus bazele teoretice ale paradigmei de programare structurată demonstrând că orice program scris folosind structura de control goto poate fi rescris folosind doar secvență , iterație și structuri de control alternative . Împreună cu criticile menționate mai sus, acest rezultat a contribuit la sfârșitul programării bazate pe goto . Toate limbile moderne oferă un set de structuri de control ale celor trei tipuri introduse de Böhm și Jacopini, deși mulți păstrează , de asemenea , got (deși nu recomandăm utilizarea acestuia fără discernământ).

Alternativă

Structurile de control "alternative" vă permit să specificați că o instrucțiune sau un bloc de instrucțiuni dat trebuie executat "(numai) dacă" o anumită condiție este valabilă. Prin urmare, ele sunt numite și structuri condiționale .

Alternativă if-then și if-then-else

Alternativa if-then (dacă-atunci) este cea mai simplă formă de alternativă. Înțelesul său poate fi parafrazat cu sintagma „dacă condiția C se menține, executați instrucțiunea (bloc) I ”. Majoritatea limbajelor de programare admit, de asemenea, (ca variantă) forma mai articulată if-then-else ( dacă-atunci-altfel ), care poate fi parafrazată ca: „dacă se menține condiția C , executați instrucțiunea (bloc) I1 ; altfel executați instrucțiune (bloc) I2 ".

Casele alternative

Alternativa de caz poate fi asemănată cu un lanț if-then-else cu anumite restricții. În acest caz, alegerea uneia dintre N instrucțiuni sau blocuri alternative se face pe baza valorii unei variabile sau expresii date , de obicei de tip întreg . Poate fi parafrazat după cum urmează: "evaluați valoarea N ; dacă valoarea sa este V1 , executați I1 ; dacă este V2 , executați I2 (etc.)". Următorul fragment de cod pseudo-C ilustrează conceptul:

 alegerea cazului Meniul de 
 1: openFile (); 
 2: closeFile (); 
 3: saveFile ();
 4: exit ();
Sfârșit;

Faptul că valoarea N trebuie (deseori) să fie de tip întreg este legat de considerații de eficiență în implementarea mecanismului. De fapt, cazul se pretează să fie transformat, la nivelul limbajului mașinii , într-un goto cu adresare indirectă , care ar putea fi bazat pe un tabel în memorie din care N selectează o anumită intrare, în care adresa instrucțiunilor către se specifică fi executat.pentru acea valoare de N.

Repetare

Pictogramă lupă mgx2.svg Același subiect în detaliu: iterație .

Structurile de control "iterative" vă permit să specificați că o instrucțiune sau un bloc de instrucțiuni dat trebuie executat în mod repetat. Se mai numesc cicluri . Fiecare structură de control de acest tip trebuie să permită specificarea în ce condiții trebuie să se încheie iterația (repetarea) acestor instrucțiuni, adică condiția de terminare a buclei sau, în mod echivalent, condiția de permanență în buclă . Cele mai cunoscute structuri de control din această categorie sunt examinate mai jos.

Pentru buclă

Pictogramă lupă mgx2.svg Același subiect în detaliu: pentru buclă .

Bucla for este utilizată atunci când cel mai natural mod de a exprima condiția de persistență într-o buclă este de a specifica de câte ori ar trebui repetată instrucțiunea sau blocul controlat de buclă. Formele tradiționale de bucle pot fi pentru parafrazat ca „repeat (codul de control) pentru i trecerea de la o anumită valoare inițială până la o anumită valoare finală, cu un anumit pas“. i este, în general, o variabilă de tip întreg , numită contor . Următorul fragment de cod BASIC ilustrează conceptul:

 PENTRU I = 1 LA 9 PASUL 2
 IMPRIMĂ I
APOI EU 

Acest fragment repetă instrucțiunea de a imprima pe ecranul variabilei contorului I. Începe de la valoarea inițială 1 pentru a ajunge la valoarea finală 10. Indicația „pasului” (STEP) specifică modul în care valoarea lui I variază de la una iterație la următoarea. Fragmentul va imprima apoi secvența 1, 3, 5, 7, 9.

În timp ce bucla

Bucla while ( while sau atâta timp cât ) este indicată atunci când condiția de persistență într-o buclă este o condiție booleană generică, independent de numărul de iterații efectuate. Formele tradiționale în buclă în timp ce pot fi parafrazate ca „repetare (codul controlat) atâta timp cât condiția C rămâne adevărată”. Un exemplu tipic este citirea datelor dintr-un fișier a cărui dimensiune nu este cunoscută a priori; s-ar putea să ia forma „citiți următoarele date până la sfârșitul fișierului”. Următorul fragment de cod pseudo- C arată un exemplu de timp :

 parola utilizator = readPassword ();
while (parola utilizator <> parola corectă) {
 reportErrorePassword ();
 parola utilizator = readPassword ();
}

În timp ce C (și multe alte limbi), condiția de permanență în buclă este verificată înainte de a executa prima iterație a buclei în sine; dacă este imediat falsă, instrucțiunile din buclă nu sunt executate . În exemplul de mai sus, dacă parola citită este corectă, blocul de cod care semnalează eroarea de intrare și solicită reintroducerea parolei (evident) nu este executat.

Buclă până la buclă

Bucla-până ( repetă până ) diferă de timp în două moduri. În primul rând, se asigură că cel puțin o iterație a buclei este întotdeauna efectuată (condiția care controlează bucla este verificată după terminarea primei iterații). În al doilea rând, condiția de încheiere a ciclului este exprimată mai degrabă decât cea a permanenței în ciclu (această din urmă diferență nu are totuși un impact conceptual foarte important, deoarece cele două condiții pot fi exprimate pur și simplu ca negare una a celeilalte). Următorul fragment pseudo- Pascal ilustrează conceptul:

 buclă
 parola utilizator: = readPassword ();
până (userpassword = parola corectă)

Variante de while și loop-until

Cele două diferențe menționate mai sus între while și loop-until sunt de fapt independente una de alta, astfel încât alte două combinații sunt ușor de imaginat (deși mai puțin răspândite din motive istorice): bucle care garantează o iterație, dar în care se specifică condiția de permanență în buclă și bucle care admit 0 iterații, dar în care este specificată condiția de terminare a buclei. Un exemplu al primului caz este structura do-while (atâta timp cât face) a lui C și a derivaților săi , exemplificată în acest fragment de pseudo-cod:.

 do
 parola utilizator = readPassword ();
while (parola utilizator <> parola corectă);

Iterare bazată pe colecție

Unele limbi (de exemplu Smalltalk , Perl , C # , Java ) oferă variante ale buclei for în care „contorul” este o variabilă generică (nu neapărat un număr întreg) care ia o secvență de valori de tipul corespunzător , de exemplu, toate valorile conținute într-o matrice sau o colecție . Următorul fragment de cod C # ilustrează conceptul:

 foreach (șiruri în aStringCollection) {print (s); }

Încetarea timpurie a buclelor și iterațiilor

Multe limbi oferă o instrucțiune specifică pentru a termina o buclă "prematur", adică a determina terminarea ei într-un mod alternativ la cea principală a buclei (de exemplu, pe baza valorii contorului în for sau pe adevărul / falsitatea unei condiții în buclele while sau repetarea până la ). Următorul fragment ilustrează conceptul într-o buclă Java:

 // căutați o valoare N într-o matrice
boolean găsit = false;
for (int i = 0; i <100; i ++)
 if (v [i] == N) {
  găsit = adevărat;
  pauză;
 }
}

În acest fragment Java, programul trebuie să stabilească dacă un număr dat N este conținut într-o matrice . De îndată ce N este găsit, devine inutil să continuăm să traversăm matricea în sine: pauza termină bucla for .

Utilizarea nediscriminatorie a mecanismelor precum break (oferită de limbaje precum C , C ++ și Java ) este adesea criticată și descurajată, deoarece se pierde o proprietate utilă de lizibilitate a buclelor de programare structurate: de fapt, când se confruntă cu un timp bucla cu o condiție C , cititorul tinde să presupună că, la sfârșitul ciclului, C este fals. Utilizarea constructelor de tip break sau similare, introducerea „unui alt” posibil motiv pentru terminarea buclei (în plus, potențial „ascuns” în blocul controlat de timp ) face ca această presupunere să nu fie complet sigură. Cu toate acestea, există și cazuri în care starea programului la sfârșitul ciclului, cu sau fără pauză , este aceeași, astfel încât problema de mai sus nu apare. Acesta este cazul cu exemplul Java de mai sus (contorul i este local pentru for și, prin urmare, este alocat la sfârșitul buclei).

Un mecanism similar (în general considerat mai puțin periculos) este cel care vă permite să încheiați prematur o anumită iterație a unei bucle, trecând imediat la următoarea. Vezi următorul program:

 pentru (int i = 1; i <= 100; i ++)
 if (număr (numere) prim) {)
  tipărit (e);
  continua;
 }
 // calculați și tipăriți factorii i
}

Acest program tipărește factorii tuturor numerelor întregi de la 1 la 100. Dacă i este un număr prim, doar tipăriți numărul în sine; în caz contrar, sunt necesare alte operații pentru al defecta. Instrucțiunea continue indică faptul că iterația curentă este terminată și face ca bucla să treacă imediat la următoarea.

Cuibărit

Toate structurile de control pot fi imbricate împreună sau inserate una în cealaltă în codul programului, respectând sintaxa limbajului utilizat.

Structuri de control nelocale

În majoritatea limbajelor de programare la nivel înalt , execuția unui program evoluează prin contexte diferite, în care unele acțiuni sunt posibile și nu altele. De exemplu, executarea unui subrutină are loc într-un context care poate fi de obicei descris în termeni de înregistrare de activare , care include date locale la care numai acea activare de subrutină poate accesa. În plus, sunt stabilite reguli foarte precise care stabilesc modul și momentul în care executarea programului tranzitează dintr-un context în altul (în cazul subrutinelor, aceste reguli sunt, în general, în concordanță cu modelul stivei de înregistrări de activare . Pentru structurile de control care nu sunt locale înseamnă acelea structuri de control care determină un salt al fluxului de control , care este independent de (poate constitui o excepție de la) aceste norme. goto este cazul extrem, și , de fapt , în mare măsură bazat pe goto limbi fără restricții (cum ar fi limbile de mașini ) sunt adesea caracterizate prin noțiuni de context foarte slabe sau complet absente.

În limbile structurate există uneori structuri de control non-locale care ocolesc restricțiile normale asupra evoluției contextului unui program în desfășurare, fără a fi totuși complet incompatibile cu acestea; acestea pot fi definite ca structuri de control nelocale structurate . Cele mai cunoscute exemple de structuri de control nelocale (atât nestructurate, cât și structurate) sunt prezentate mai jos și se referă la problema generală a gestionării excepțiilor .

Condiții în PL / 1

Limbajul PL / 1 are un mecanism de manipulare a excepțiilor destul de simplu. Există o serie de condiții (citiți: excepții, situații anormale; de ​​exemplu, încercări de a împărți un număr la 0 sau de a accesa o matrice cu o valoare index ilegală) care sunt automat „ridicate” (RAISE) de limbă. Programatorul poate indica ce trebuie să facă atunci când o condiție este ridicată printr-o clauză din forma „ON <condiție> <acțiune>”. În majoritatea cazurilor, <acțiunea> care trebuie efectuată atunci când apare <condiția> este specificată sub forma unui goto . Mecanismul PL / 1 poate fi considerat ca o versiune primitivă (și puțin sau deloc „structurată” a gestionării excepțiilor în C ++ și în limbaje derivate.

Excepții în C ++ și limbile derivate

Pictogramă lupă mgx2.svg Același subiect în detaliu: gestionarea excepțiilor .

C ++ , D , Java și C # tratează excepțiile cu o structură de control nelocalizată special structurată, numită de obicei o structură try-catch , a cărei sintaxă este prezentată mai jos:

 încerca {
 ...
 ... // cod care poate provoca excepții de diferite tipuri
 ...
}
catch (Exception Type e) {
  ... // rezolvați problema
}
catch (UnAltroTipoDiExcezione e) {
  ... // rezolvați problema
}
in cele din urma {
  ... // cod care trebuie executat oricum
}

În această schemă, blocul de cod controlat de try conține una sau mai multe instrucțiuni care pot provoca o excepție. Dacă se întâmplă acest lucru, controlul sare din contextul de bloc asociat cu încercarea trecând la un bloc controlat de o captură (așa cum sugerează exemplul, puteți avea mai multe capturi asociate cu diferite tipuri de excepții). Blocul de captare este tratatorul excepției , adică conține acele operațiuni care constituie, în sens larg, „contramăsura” prevăzută de programator în cazul în care apare o excepție specială. Blocul finally- controlat (găsit în D , Java și C # ) conține declarații care trebuie executate oricum, indiferent dacă sunt sau nu o excepție are loc ( în mod normal , eliberator operațiuni de resurse , cum ar fi închiderea fișierelor sau conexiunile sunt plasate în blocul controlat finally- rețea ; pentru astfel de situații C # are, de asemenea, un alt construct ad hoc , clauza de utilizare ).

Dacă se ridică o excepție într-un bloc de încercare pentru care nu era așteptată nicio captură (sau dacă apare o excepție undeva în cod care nu este verificată de o încercare ), subrutina sau metoda curentă se termină și excepția este propagată către subrutina sau metoda apelantă , care îl va prinde dacă declarația de apel subrutină eșuată este inclusă într-un bloc try cu o captură asociată adecvată pentru acel tip de excepție; dimpotrivă, apelantul însuși se va termina și excepția va fi propagată în continuare „în sus” (către apelantul apelantului). Se pot face două observații asupra acestui model de management:

  • poate fi definit ca structurat în sensul că, în timp ce sare dintr-un context în altul în conformitate cu alte reguli decât cele care „în mod normal” reglementează schimbarea contextului unui program structurat, nu încalcă principiile sale fundamentale: controlul nu poate trece la un indicați oricare dintre programe (ca în Goto ), dar respectă modelul stivei de înregistrări de activare (trecerea din contextul apelului la cel al apelantului);
  • se justifică prin faptul că, în practica de programare, nu toate excepțiile pot fi gestionate eficient „local”; adesea, luarea de contramăsuri pentru o anumită problemă necesită informații suplimentare care sunt disponibile numai într-un context mai larg. De exemplu, dacă un eșec la deschiderea unui fișier trebuie raportat utilizatorului cu un mesaj într -o fereastră pop-up , nu este rezonabil să ne așteptăm ca acest lucru să fie făcut de o rutină generică de acces la fișiere (al cărei design, din motive de reutilizare , este probabil nu „presupune” că aplicația curentă are mai degrabă o interfață grafică decât o interfață text).

Un model similar cu cel descris tocmai se găsește în limbile Python , Ruby , Objective C și altele.

Structuri de control concurente

În contextul programării simultane și paralele , au fost introduse structuri de control specifice care specifică sau implică executarea simultană conceptuală a anumitor seturi de operații sau instrucțiuni. Cel mai reprezentativ limbaj în acest sens este probabil limbajul de programare paralel Occam . Acest limbaj oferă cel puțin două structuri de control inovatoare, respectiv pentru executarea paralelă a instrucțiunilor și o formă specială de alternativă care implică evaluarea paralelă a condițiilor care o guvernează.

Structura PAR a Occam

Structura de control PAR specifică faptul că un anumit set de instrucțiuni trebuie executat în paralel. În forma sa cea mai simplă, structura PAR are următoarea sintaxă:

 PAR
 x: = x + 1
 y: = y + 1

În acest fragment de cod, incrementarea celor două variabile are loc simultan. PAR admite, de asemenea, o formă mai complexă, care are unele asemănări cu o buclă for , și este menționată în mod constant cu cuvintele cheie PAR-FOR. Următorul fragment de cod dobândește un număr întreg dat din patru canale paralele.

 PAR i = 0 PENTRU 4
 INT n
 Acolo] ? n [i]

Analogia cu bucla for este despre utilizarea „contorului” i. La fel ca o buclă tradițională pentru buclă, fragmentul de cod afișat face cele de mai sus de cinci ori , „pentru i variind de la 0 la 4”; cu toate acestea, cele cinci operații de intrare nu sunt efectuate secvențial, ci în paralel.

Structura ALT a lui Occam

Construcția ALT vă permite să definiți un set de „comenzi păzite”. O comandă păzită constă dintr-o condiție numită pază și o declarație, cu o anumită analogie cu o structură if . Cu toate acestea, semnificația comenzii păzite nu este că declarația va fi executată dacă condiția este adevărată; mai degrabă, declarația poate fi executată atunci când condiția devine adevărată. Construcția ALT grupează o serie de comenzi păzite; se execută prima comandă pentru care garda devine adevărată. Dacă există mai multe comenzi a căror protecție este adevărată, una dintre ele este selectată (în mod arbitrar) de mașina virtuală de limbaj.

 ALT
 într-o? v
  afară! v
 în B? v
  afară! v

Construcția ALT prezentată aici include două comenzi cu gardă. În acest caz, gardienii sunt instrucțiuni ( suspensive ) care așteaptă în intrare o valoare de la unul din cele două canale ( în.a și in.b ). De îndată ce datele devin disponibile pe oricare dintre cele două canale, acestea vor fi achiziționate, garda corespunzătoare va fi considerată „adevărată”, iar instrucțiunea asociată (de ieșire) va fi executată, încheind execuția blocului ALT.

Bibliografie

Mergi la
Structuri de control al programării structurate

Elemente conexe