Instrucțiuni pentru structuri de control repetitive

În limbajul Java, structurile de control repetitive se realizează prin trei instrucțiuni:
    - instrucțiunea  while,  prin care se programează ciclul cu test inițial;
    - instrucțiunea  do..while, prin care se programează ciclul cu test final;
    - instrucțiunea  for,  care este o varianta modificată a ciclului cu test inițial, astfel încât să poată fi folosită și la realizarea ciclului cu contor.

În cele ce urmează se vor prezenta instrucțiunile menționate mai sus.

Instrucțiunea  while (ciclul cu test inițial)

Această instrucțiune are forma generală

    while (condiție)
        instrucțiune

în care
    condiție - este o expresie de tip  boolean;
    instrucțiune- poate fi orice instrucțiune (simplă sau structurată) a limbajului Java, inclusiv altă instrucțiune  while.

Executarea acestei instrucțiuni se face astfel: se evaluează expresia booleană condiție și - dacă valoarea acesteia este  true, deci condiția este satisfacută - se execută  instrucțiune,după care se revine la evaluarea condiției. acest ciclu se repetă cât timp este satisfacută condiția.
 
Exemplul 1.  Se va calcula în mod repetat valoarea expresiei   2e-0.25ksin(0.1k-0.08) pentru valori întregi ale variabilei  de la 0 la n, unde n este dat.

În pseudocod, programul corespunzător se poate scrie astfel:

    <#1. Se introduce valoarea lui n, de tip intreg>
    <#2. Se calculează și se afișează valoarea expresiei 2e-0.35ksin(0.17k-0.08)
         pentru valori ale lui k de la 0 la n >

Rafinarea pseudoinstrucțiunii #1 se poate face direct în program. La rafinarea pseudoinstrucțiunii #2 luăm în considerație caracterul repetitiv al acțiunii solicitate, astfel că putem folosi structura de ciclu cu test inițial, iar programul devine:

    <#1. Se introduce valoarea lui n, de tip intreg>
    /* #2. Se calculează și se afisează valoarea expresiei */
    k=0; // Se inițializează k la prima sa valoare
    cât_timp k<=n
        afișează_valoarea_expresiei 2e-0.35ksin(0.17k-0.08);
        k=k+1; // se trece la valoarea următoare a lui k
    sfârșit_ciclu

Programul corespunzător în limbajul Java se găsește în fișierul  Repetare1.java.

Exemplul 2. Calcularea unei sume cu număr finit de termeni.

Considerăm că se cere să se calculeze suma

în care sunt date valorile variabilelor  a și b, de tip real și n de tip întreg.

Aplicând tehnica rafinărilor succesive, vom da pentru început programului următoarea structură secvențială:

    <#1. Se introduc datele de intrare n de tip întreg și a,b de tip real>
    <#2. Se calculează suma S>
    <#3. Se afișează valoarea lui S>

Rafinarea pseudoinstrucțiunilor #1 și #3 se poate face direct în program. Pentru rafinarea pseudoinstrucțiunii #2, remarcăm că suma  S se poate calcula în modul următor: se pornește de la valoarea inițiala  S=0 și se adaugă la aceasta succesiv toți termenii sumei, pentru valori ale lui k de la 0 la n. Acțiunea are un caracter repetitiv, deci se poate folosi în acest scop un ciclu cu test inițial. Se obține astfel programul următor în pseudocod:

    <#1. Se introduc datele de intrare n de tip întreg și a,b de tip real>
    /* #2. Se calculează S */
    S=0; // Se atribuie lui S valoarea inițială
    k=0; // Se atribuie lui k valoarea inițială
    cât_timp k<=n
        <#2.1. Se calculează termenul curent al sumei și se adaugă la
                valoarea precedentă a lui S >
        k=k+1; // Se trece la valoarea următoare a lui k
    sfârșit_ciclu
    afisează S;

Programul corespunzător în limbajul Java se găseste în fișierul Suma1.java.

Exemplul 3. Calcularea sumei unei serii convergente.

Fie S suma seriei convergente

S=1+x+x2/(2!)+x3/(3!)+...+xk/(k!)+...

în care k! este factorialul lui k. Demonstrarea existentei acestei sume (deci a convergenței seriei) este de domeniul analizei matematice. Întrucât, însă, noi știm că această serie este convergentă, putem să scriem un program pentru calcularea sumei S. Seria are un număr infinit de termeni, deci valoarea ei exactă nu poate fi calculată direct, prin adunarea lor. Totuși, valoarea aproximativă a sumei S se poate obține prin "trunchiere", adică luând în considerație numai un număr finit de termeni. Vom calcula deci, de fapt, suma parțială:

Sn=1+x+x2/(2!)+x3/(3!)+...+xn/(n!)

cu observația că numărul de termeni n nu este dinainte cunoscut.

Se știe (si se observă cu ușurință) că termenii seriei sunt descrescători ca modul. Ne putem deci opri din evaluarea sumei, în momentul în care constatăm că valoarea termenului curent este mult mai mică decât suma parțială deja calculata. Putem deci adopta, pentru calcularea sumei, următorul program în pseudocod:

    <#1. Se introduce valoarea lui x, de tip real>
    S=0; // Se inițializează suma S la valoarea 0
    k=0; // Se inițializează indicele termenului curent la valoarea 0
    t=1; // Se inițializează termenul curent la valoarea 1 (cea a primului termen)
    /* Se calculează suma S a seriei */
    cât_timp (<#2.termenul curent t este semnificativ>)
        S=S+t; // Se adaugă la suma precedentă S valoarea termenului curent
               // și se obține noua valoare a sumei S
        k=k+1; // Se trece la indicele termenului următor;
        <#3.se calculează noua valoare t a termenului următor>
    sfârșit_ciclu
    afișează S.

Pentru a rafina pseudocondiția #2 avem în vedere că, în memoria calculatorului, numerele reale au lungime finită. În consecință, dacă adunăm două numere foarte diferite ca ordin de mărime, "suma" lor va fi egală cu cel mai mare dintre ele. De exemplu, dacă reprezentarea numerelor reale s-ar face în sistemul zecimal cu 6 cifre semnificative, atunci suma 32.7628+0.00006924 ar fi egala cu numarul cel mai mare, adica 32.7628, deoarece cifrele de la rezultat situate după cea de a 6-a cifra semnificativă se pierd. În cazul seriei noastre, la un moment dat termenul curent t va deveni atât de mic ca modul, încât adunat la suma deja calculata S nu o va mai modifica. Vom considera deci că termenul t este semnificativ, cât timp se îndeplinește condiția (S+t este_diferit_de S).
Pentru a rafina #3, observăm că între valorile termenului t la doi pași succesivi k șk-1 exista relația:

tk=tk-1*x/k.

Întrucât în program nu este necesar să folosim indicii pașilor, vom scrie această relație sub forma  t=t*x/k, adică se atribuie ca nouă valoare a termenului curent t valoarea precedentă a acestui termen, înmulțită cu raportul x/k.

Cu observațiile de mai sus, programul de calculare a sumei seriei devine:
 

    <#1. Se introduce valoarea lui x, de tip real>
    S=0; // Se inițializează suma S la valoarea 0
    k=0; // Se inițializează indicele termenului curent la valoarea 0
    t=1; // Se inițializează termenul curent la valoarea 1 (cea a primului termen)
    /* Se calculează suma S a seriei */
    cât_timp (S+t este_diferit_de S)
        S=S+t; // Se adaugă la suma precedentă S valoarea termenului curent
               // și se obține noua valoare a sumei S
        k=k+1; // Se trece la indicele termenului următor;
        t=t*x/k; // se calculează noua valoare t a termenului următor
    sfârșit_ciclu
    afișează S.

Programul corespunzător în limbajul Java este dat în fișierul Serie1.java. Pentru a înțelege mai bine acest program, recomandăm să calculați 3-4 termeni manual pentru a urmări cum se înlănțuie aceștia.
 

Instrucțiunea do..while (ciclul cu test final)

Forma generală a instrucțiunii de control, prin care se realizează în limbajul Java ciclul cu test final este următoarea:

    do
        instrucțiune
    while (condiție);

unde, la fel ca în cazul instrucțiunii  while,condiție este o expresie de tip boolean, iar instrucțiune este orice instrucțiune (simplă sau structurată) din limbajul Java. Remarcăm că la sfârșitul acestei instrucțiuni (după condiție) se pune obligatoriu ';' (punct și virgulă).

 Principala  deosebire față de instrucțiunea  while pentru ciclul cu test inițial  este că testarea condiției se face după ce a fost executată instrucțiunea din corpul ciclului. În consecință, corpul ciclului va fi executat cel puțin o dată.

În programul din fișierul DeosCicluri.java se face o comparație între funcționarea ciclurilor cu test inițial și cu test final în condiții similare.
 
În programele din fișierele Repetare2.java,Suma2.java și Serie2.java se reiau exemplele din secțiunea precedentă (și realizate în fișierele Repetare1.java, Suma1.java și Serie1.java), dar folosind de data aceasta ciclul cu test final.

Instrucțiunea for (ciclul cu contor generalizat)

În unele limbaje de programare, pentru realizarea ciclurilor la care numărul de pași este dinainte cunoscut, se folosește o instrucțiune de control specială, numită "ciclu cu contor". În pseudocod, această instrucțiune poate fi scrisă în modul următor:

    pentru variabila de_la val_init la val_fin [cu_pasul pas] execută
        <instrucțiune sau secvență de instrucțiuni>
    sfârșit_ciclu

în care, de regulă, variabila este o variabilă (de regulă de tip întreg), numită și contorul ciclului,val_init și val_fin sunt respectiv valorile inițială și finală ale acestei variabile, iar pas este pasul de variație la fiecare parcurgere a ciclului. Pasul implicit este 1.
 
De exemplu, pentru a calcula produsul P=(a+5)*(a+6)*(a+7)*...*(a+n) se vor scrie următoarele pseudoinstrucțiuni:

    P=1;
    pentru k de_la 5 la n execută
        P=P*(a+k);
    sfârșit_ciclu

Remarcăm că, în acest exemplu, instrucțiunea P=1 nu face parte din ciclu, dar ea a fost necesară pentru a da valoarea inițială a variabilei P.

În mod similar, pentru a calcula suma  S=7+10+13+16+...+31 se pot folosi instrucțiunile

    S=0;
    pentru j de_la 7 la 31 cu_pasul 3 execută
        S=S+j;
    sfârșit_ciclu

Remarcăm că, pentru aceleași calcule, se pot folosi și  ciclurile  cu test inițial sau cu test final. Astfel, primul din aceste exemple, poate fi rescris folosind ciclul cu test inițial astfel:

    P=1;
    k=5;
    cât_timp k<=n execută
        P=P*(a+k);
        k=k+1;
    sfârșit_ciclu

Remarcăm că, la trecerea de la ciclul cu contor la ciclul cu test inițial, s-au introdus următoarele instrucțiuni suplimentare:
    - înainte de intrarea în ciclu, s-a pus instrucțiunea k=5, prin care se atribuie variabilei-contor valoarea inițială;
    - la sfârșitul corpului ciclului s-a introdus instrucțiunea  k=k+1,  prin care se trece la valoarea următoare a variabilei-contor;
    - in instrucțiunea while s-a pus drept condiție compararea variabilei-contor cu valoarea ei finală.
Lăsăm cititorului ca exercițiu să scrie același program, folosind ciclul cu test final.

În limbajul Java, urmând tradiția limbajului C de la care acesta a preluat o bună parte a regulilor de sintaxă, se folosește o instructiune generalizată, prin care se realizează atât ciclul cu contor, cât și ciclul cu test inițial. Această instrucțiune are forma:

    for ([inițializare]; [condiție] ; [trecere_la_pasul_următor] )
       [<instrucțiune_corp_ciclu>]

în care:

    inițializare ::= instructiune[,instructiune]*- este formată din una sau mai multe instrucțiuni, separate prin virgule, care se execută înainte de intrarea în ciclu, astfel că sunt folosite, de regulă, pentru inițializarea acestuia;
    condiție - este o expresie de tip  boolean folosită drept condiție de continuare a ciclului cu test inițial (condiția este testata înainte de a se executa corpul ciclului;
    trecere_la_pasul_următor := instrucțiune[,instrucțiune]* - una sau mai multe instrucțiuni, separate prin virgule, care se execută după ce a fost executat corpul ciclului. astfel că sunt, în general, folosite pentru pregătirea trecerii la următoarea parcurgere a ciclului;
    <instrucțiune_corp_ciclu>- o instrucțiune (simpla sau structurată) care constituie corpul ciclului.
 
Respectând aceasta formă generală, calcularea produsului dat ca exemplu mai sus în pseudocod, se programează în limbajul Java în modul urmator:

    for(P=1,k=5; k<=n; k++)
        P*=a+k;

Se observă ca se inițializează P la valoarea 1 și k la valoarea 5, apoi cât timp este satisfacută condiția k<=n se execută instrucțiunea P*=a+k, după care se execută instrucțiunea k++ (se trece la următoarea valoare a lui k).

După cum se vede în forma generală, diferitele componente ale acesteia sunt opționale; în consecință, instrucțiunea for poate fi scrisă sub diferite forme echivalente, de exemplu:
a)
    P=1;
    k=5;
    for(;k<=n;){
        P*=a+k;
        k++;
    }

Observăm că, de data aceasta, instrucțiunea  for(;k<=n;) are exact același efect, pe care l-ar fi avut instrucțiunea  while(k<=n). Remarcăm, de asemenea, că cei doi separatori ';' din interiorul parantezei au fost mentinuți, chiar dacă lipsesc zonele de inițializare și de incrementare.

b)
    for(P=1,k=5;k<=n;P*=a+k,k++);

În ultimul caz, corpul ciclului este constituit numai dintr-o instrucțiune vidă, reprezentată prin carecterul ';' pus dupa paranteza închisă.  Aceste exemple  sunt date și în programul din fișierul TestFor.java. Tot acolo se arată că variabila care servește drept contor al ciclului  poate fi, în limbajul Java, și de tip real, iar creșterea acesteia de la o parcurgere la alta a ciclului poate fi de asemenea de tip real și nu este obligatoriu să fie constantă. Considerăm că exemplele din acest program sunt suficiente pentru a arăta ca instrucțiunea for permite programatorului posibilități de utilizare foarte diverse, cu condiția ca acesta să înțeleaga corect modul ei de funcționare.

Declararea unor variabile în zona de inițializare a ciclului  for

Este permis ca în zona inițializare a ciclului for să fie incluse și unele declarații de variabile. Aceste variabile sunt tratate drept variabile locale ale ciclului for respectiv  și deci nu sunt vizibile (disponibile) după ieșirea din ciclu, putând fi redefinite. Iată un astfel de exemplu:

    x=0;
    for(int k=0; k<n; k++)
        S+=a*k;

În acest caz, variabila k a fost atât declarată, cât și inițializată, în zona de inițializare a instrucțiunii for. În consecință această variabilă este disponibilă în interiorul acestei instrucțiuni, inclusiv în corpul ciclului, dar își pierde valabilitatea la ieșirea din acest ciclu.
Daca în zona de inițializare apare o declarație, ea trebuie să fie singura instrucțiune din această zonă. Astfel, în exemplul precedent nu ar fi fost permis, de exemplu, să se scrie   for(x=0, int k=0; k<n; k++) S+=a*k;
Este însă permis să se declare în zona de inițializare mai multe variabile de același tip. Astfel, exemplul anterior poate fi modificat după cum urmează:

    x=0;
    for(int k=0,i=9; k<n; k++, i--)
        S+=(a+i)*k;

Aceste exemple și altele similare se găsesc în programul din fișierul TestFor1.java.
 
Exemple de programe în care se folosește ciclul for

În fișierele  Repetare3.java, Suma3.java și Serie3.java se reiau exemplele date in fisierele Repetare1.java, Suma1.java și Serie1.java), dar folosind instrucțiunea  for în loc de while. Vă recomandăm să comparați în mod special programele din fișierele Serie1.java si Serie3.java si să explicați deosebirile constatate.



© Copyright 2000 - Severin BUMBARU, Universitatea "Dunărea de Jos" din Galați