Pętle w języku C++ - pętla do while

Sens poznania innego rodzaju pętli

Jeśli zastanawiasz się, czy warto poznawać kolejny rodzaj pętli, to rzeczywiście Twoje wątpliwości nie są bezpodstawne. Musisz wiedzieć, że za pomocą wszystkich pętli można wykonać w zasadzie te same działania.

Po co zatem uczyć się kilku typów, skoro można by było tylko jednego? Odpowiedź jest prosta - dla wygody. To co za pomocą jednego typu pętli można wykonać bardzo prosto, niekiedy wymaga przekształceń, aby zadziałało dokładnie tak samo przy użyciu innej pętli.

Jeśli nawet to Cię nie przekonuje, to niech przekona Cię fakt, że wszyscy programiści używają wszystkich rodzajów pętli dostępnych w języku. Zatem jeśli kiedykolwiek będziesz miał za zadanie przeanalizować kod napisany przez kogoś innego, chyba nie warto najeść się wstydu, nie rozumiejąc tak prostej rzeczy, jaką jest jedna z pętli w języku C++.

Podstawy pętli do while

Pętla do while podobnie jak pętla for i podobnie jak wszystkie pozostałe pętle, umożliwi nam powtórzenie określonych operacji tak długo jak warunek końcowy jest spełniony. Schematyczna postać pętli wygląda następująco:

do
{
  lista_instrukcji
}
while (warunekKoncowy);

Lista instrukcji może stanowić jedną instrukcję lub ich grupę. Warto jednak zaznaczyć, że nawiasy klamrowe są konieczne nawet jeśli chcemy wykonać tylko jedną instrukcję.

Warunek końcowy jest tylko jeden, ale może występować jako złożony warunek (stworzony za pomocą operatorów logicznych).

Warto teraz zastanowić się jak działa pętla do while. W pierwszym kroku jest wykonywana lista instrukcji zawarta między nawiasami klamrowymi. Następnie jest sprawdzany warunek - jeśli jest on prawdziwy, wówczas pętla wykonuje się ponownie. Pętla wykonuje się do momentu, gdy warunek końcowy będzie fałszywy.

Co to oznacza w praktyce? Oznacza to, że lista instrukcji pętli do while wykona się co najmniej jeden raz. Łatwo to zapamiętać, gdy przyjrzymy się postaci pętli - najpierw jest lista instrukcji (czyli najpierw zostaje wykonana lista instrukcji) a dopiero później znajduje się warunek (czyli dopiero teraz warunek zostaje sprawdzony).

Jest to bardzo ważna kwestia i odróżnia ona pętlę do while od pętli for. W pętli for warunek był sprawdzony przed wykonaniem jakiejkolwiek instrukcji, czyli w rezultacie mogło się zdarzyć tak, że lista instrukcji nie została nigdy wykonana. Tutaj natomiast lista instrukcji zostanie wykonana co najmniej jeden raz.

Spójrz teraz na poniższy przykład, a następnie go uruchom. Przy okazji przypomnij sobie skróty klawiszowe przedstawione w poprzedniej lekcji, umożliwiające przerywanie działania, kiedy program się zapętli.

#include <iostream>

using namespace std;

int main()
{  
 int ile=3;
 do
 {
    cout <<"Pierwsza instrukcja"<<endl;  
    cout <<"Druga instrukcja"<<endl;
 }
 while (ile<10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.1

Zastanawiasz się pewnie co takiego się stało, że program uległ zapętleniu. Zastanówmy się wspólnie. Zmienna ile miała początkową wartość 3. Program wykonał instrukcje znajdujące się pomiędzy nawiasami klamrowymi i przeszedł do sprawdzenia warunku. Warunek jest prawdziwy, bo 3<10 jest prawdą. Zatem program ponownie wykonał instrukcje znajdujące się w nawiasach klamrowych i po raz kolejny sprawdził warunek. I jak się okazało, warunek znów jest prawdziwy, bo zmienna ile ma znów wartość 3.

Mam nadzieję, że teraz już wiesz w czym tkwi problem. Zmienna sterująca pętlą (czyli w tym przypadku zmienna ile) nie ulega nigdzie zmianie, zatem warunek będzie zawsze prawdziwy. Gdzie jednak możemy zmienić wartość zmiennej sterującej? W pętli for mieliśmy do tego specjalną konstrukcję. Tutaj natomiast zmianę wartości zmiennej należy wykonać wewnątrz listy instrukcji (przy okazji przypominam, że w pętli for było to też możliwe).

Poniżej zatem znajduje się pętla do while użyta zgodnie z naszymi oczekiwaniami:

#include <iostream>

using namespace std;

int main()
{  
 int ile=3;
 do
 {
    cout <<"Pierwsza instrukcja"<<endl;  
    cout <<"Druga instrukcja"<<endl;
    ++ile;
 }
 while (ile<10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.2

Nie ma tutaj co prawda nic nadzwyczajnego, zwróć jednak uwagę, że wartość zmiennej zostaje zmieniona na samym końcu listy instrukcji. Takie posunięcie jest najlepsze, bowiem gdybyśmy chcieli wypisywać wartość zmiennej sterującej i instrukcję zmiany wartości zmiennej sterującej umieścilibyśmy na przykład pomiędzy innymi instrukcjami, wówczas część obliczeń lub wypisań mogłaby być wykonywana dla "starej" wartości, a część dla "nowej".

Takie właśnie zjawisko przedstawia poniższy program. W programie będziemy chcieli wypisywać wartość liczby i wartość tej liczby do kwadratu. Poprzez zapisanie instrukcji zmiany wartości zmiennej sterującej pomiędzy innymi instrukcjami, widoczne wyniki będą niezrozumiałe:

#include <iostream>

using namespace std;

int main()
{  
 int ile=3;
 do
 {
    cout <<"Liczba to "<<ile;
    ++ile;
    cout <<" a kwadrat liczby to "<<ile*ile<<endl;
 }
 while (ile<10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.3

Mam nadzieję, że tym właśnie przykładem zachęciłem Cię do stosowania zasady, aby nie dokonywać zmiany wartości zmiennej sterującej pośród zwykłych instrukcji. Zmianę zmiennej sterującej możemy wykonać na końcu wszystkich instrukcji lub na początku instrukcji ( zależy od potrzeby), ale lepiej nie robić tego nigdy pomiędzy instrukcjami (chyba, że jest to rzeczywiście uzasadnione).

Oprócz tego pragnę dodać, że oczywiście zmienną sterującą możemy zmieniać w dowolny sposób (nie tylko zwiększać o 1), podobnie jak to miało miejsce w pętli for.

Zasady dotyczące pętli do while

Tak naprawdę chcę Ci teraz uświadomić, że musisz nauczyć się samodzielności. Czy w pętli do while możemy używać dwóch zmiennych sterujących? Czy również może pojawić się problem z liczbami bez znaku? Czy w pętli do while możemy zagnieżdżać inne pętle do while? A może możemy w niej zagnieżdżać również inne rodzaje pętli?

Na każde z postawionych wyżej pytań odpowiedź brzmi tak. Jeśli to nie było dla Ciebie takie oczywiste, nie musisz się tego wstydzić, jednak o tak oczywistych sprawach nie będę już wspominał w kolejnych lekcjach.

Od teraz musisz zacząć naukę przez analogię. Jeśli coś było dozwolone w innym miejscu, to najprawdopodobniej taka konstrukcja jest również możliwa w zagadnieniu, które właśnie omawiam. Na wszystkie pytania, które przed momentem postawiłem w Twoim imieniu, możesz znaleźć odpowiedzi w lekcji dotyczącej pętli for.

Należy kojarzyć - pętla for to była pętla i pętla do while to też pętla. Zatem najprawdopodobniej większość reguł jest identyczna. Analogicznie, jeśli kiedyś wspomniałem, że nawiasy klamrowe służą do grupowania instrukcji, to taką regułę można zastosować w prawie każdym miejscu.

Tak samo kiedyś wspominałem, że warunki logiczne mogą być albo proste albo złożone wówczas używamy operatorów logicznych. Pamiętaj, że w języku C++ taka reguła obowiązuje zawsze.

Jeśli natomiast w którymkolwiek z przypadków nie będzie to dla Ciebie takie oczywiste, że taka reguła w tym przypadku również obowiązuje, wystarczy, że sprawdzisz. Nie dość, że upewnisz się jak jest naprawdę, to przy okazji nabierzesz trochę doświadczenia programistycznego, które jest niewątpliwie bardzo ważne i cenne.

Pętla do while - praktyka

Dla ćwiczenia spróbujemy teraz przekształcić fragmenty kodów, w których użyto pętli for na kod wykonujący dokładnie to samo zadanie, ale z użyciem pętli do while.

Kody będę przedstawiał parami - najpierw kod z wykorzystaniem pętli for, a następnie z użyciem pętli do while. Zachęcam Cię jednak do samodzielnego napisania i przekształcenia programów. Dzięki temu na pewno znacznie więcej się nauczysz i zrozumiesz.

Kod 1 z użyciem pętli for:

#include <iostream>

using namespace std;

int main()
{  
 int i;
 for (i=1;i<=10; ++i)
 {
    cout <<"Wypisuje po raz "<<i;
    cout <<". A to tylko dla testu"<<endl;
 }

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.4

Kod 1 z użyciem pętli do while:

#include <iostream>

using namespace std;

int main()
{  
 int i=1;
 do
 {
    cout <<"Wypisuje po raz "<<i;
    cout <<". A to tylko dla testu"<<endl;
    ++i;
 }
 while (i<=10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.5

Kod 2 z użyciem pętli for:

#include <iostream>

using namespace std;

int main()
{  
 int ktory=1, i, j;

 for (i=1;i<=10; i+=2)
 {
    cout <<"Tak bedzie dla i="<<i<<endl;
    for (j=5;j>=2;--j)
    {
       cout <<ktory<<". raz. i*j="<<i*j<<endl;
       ++ktory;
    }
    cout <<"Tak bylo dla i="<<i<<endl;
    getchar(); // czekanie na nacisniecie ENTER
 }


 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.6

Kod 2 z użyciem pętli do while:

#include <iostream>

using namespace std;

int main()
{  
 int ktory=1, i=1, j;
 
 do
 {
    cout <<"Tak bedzie dla i="<<i<<endl;
    j=5;
    do
    {
       cout <<ktory<<". raz. i*j="<<i*j<<endl;
       ++ktory;
       --j;
    }
    while (j>=2);
    cout <<"Tak bylo dla i="<<i<<endl;
    getchar(); // czekanie na nacisniecie ENTER
    i+=2;
 }
 while (i<=10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.7

Kod 3 z użyciem pętli for:

#include <iostream>

using namespace std;

int main()
{  
 for (unsigned int i=1;i<=10;i+=1)
 {
    for (unsigned int j=1;j<=i;++j)
       cout <<i*j<<' ';
    cout <<endl;
 }

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.8

Kod 3 z użyciem pętli do while:

#include <iostream>

using namespace std;

int main()
{  
 unsigned int i=1, j;
 do
 {
    j=1;
    do
    {
       cout <<i*j<<' ';
       ++j;
    }
    while (j<=i);
    cout <<endl;
    ++i;
 }
 while (i<=10);

 cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
 getchar();
 return 0;  
}
program nr 14.9

Mam nadzieję, że już dobrze rozumiesz pętlę do while. Jak widzisz zamiany pętli for na pętlę do while nie są takie trudne. Zwróć jednak uwagę na to, że w pętlach do while zazwyczaj zmienne należy utworzyć przed najbardziej zewnętrzną pętlą (w pętli for możemy tworzyć zmienne bezpośrednio w części stanów początkowych). Dodatkowo bardzo ważną kwestię stanowi odpowiednie przypisanie wartości początkowych zmiennym sterującym.

Szczerze mówiąc, zamiany pętli for na pętlę do while nie byłyby takie proste, gdybyśmy zmienili teraz któryś z warunków w pętli for, tak, że warunek jest od razu fałszywy. Pętla do while jak już wiesz, warunek sprawdza dopiero na końcu, więc mimo, że warunek byłby fałszywy, lista instrukcji wykonałaby się jeden raz (a w pętli for lista instrukcji nie wykonałaby się ani razu, bo tam warunek jest sprawdzany na początku).

Zadanie 1

Napisz program wypisujący wszystkie liczby parzyste od 0 do 100 w postaci (p = liczba).

Zadanie 2

Napisz program obliczający sumę liczb wprowadzonych przez użytkownika. Program zakończy się kiedy suma będzie równa lub większa 200

Zadanie 3

Napisz program, który znajdzie największy wspólny dzielnik liczb A i B.

Zadanie 4

Popraw program z zadania 3 tak, aby pozwalał na kolejne obliczanie i za każdym razem pytał się czy kontynuować.

powrót