Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: pekfos
Kurs C++

Jaki warunek wpisać do pętli?

[pytanie/odpowiedź] Dokument opisuje prosty sposób na dobieranie warunków pętli

Jaki warunek wpisać do pętli?

Takie pytanie pojawia się, gdy ktoś zna narzędzia - wie jak działają pętle (do..)while, podstawowy for, ma w głowie ogólne pojęcie, co pętla powinna robić, ale nie potrafi tego zapisać formalnie w formie kodu. Niektórzy początkujący mają złudne odczucie, że potrafią posługiwać się pętlami, bo potrafią kopiować takie popularne idiomy jak, przykładowo, przejście po tablicy
C/C++
for( int i = 0; i < 10; ++i )
     std::cout << tablica[ i ] << ' ';
Taki kod jest oczywisty. Tablice indeksuje się od zera, do Rozmiar - 1. Wypisujemy wszystko po kolei, więc zwiększamy o 1. Taki kod byłby podany w kursie, w lekcji o tablicach, gdyby pętla for była wcześniej omówiona, ale to nie ma tu znaczenia, bo wszystkie pętle są między sobą zamienne.
Taka pętla jest dobra do powielania, bo jedyne co się w niej wymienia, to rozmiar tablicy i może nazwę zmiennej. Ale co dokładnie dzieje się w i < 10? Dla liczb i mniejszych od rozmiaru tablicy, a więc dążymy do rozmiaru tablicy, ale sam rozmiar tablicy już nie jest poprawną wartością. To samo można by zapisać warunkiem i <= Rozmiar - 1, co jest zapisem który czasem można spotkać na forum w tym kontekście, ale jest to niepotrzebne wydłużenie kodu.

W drugą stronę

Wypiszmy teraz elementy tablicy w przeciwną stronę:
C/C++
for( int i = Rozmiar - 1; i >= 0; --i )
     std::cout << tablica[ i ] << ' ';
Teraz kod przypomina trochę dłuższą wersję poprzedniego przykładu, tylko wszystko jest na opak. Zwróć uwagę, że teraz i nie może być typu unsigned, bo dla typów bez znaku każda liczba jest większa równa zeru i pętla byłaby nieskończona. Nie ma tego ograniczenia, jeśli dodasz 1 'do obu stron równania'.
C/C++
for( int i = Rozmiar; i > 0; --i )
     std::cout << tablica[ i - 1 ] << ' ';
Zaczynamy od i o jeden większego, kończymy na i o jeden większym i jest to skorygowane w odwołaniu do tablicy.

Co w bardziej skomplikowanym przypadku?

Aby odpowiedzieć na to pytanie, pomimo tego jak bardzo jest ogólne, trzeba zrozumieć absolutną podstawę działania pętli: powtarzaj, jeśli warunek jest prawdziwy, lub inaczej: powtarzaj, warunek będzie fałszywy. Wszystko sprowadza się do określenia albo warunku kontynuacji pętli, albo warunku przerwania pętli - jedno jest po prostu negacją drugiego, ale czasem łatwiej jest opisać problem z przeciwnej strony.

Samo sedno sprawy

Choćby wszystkie utrudnienia były sprzątnięte z drogi, nie da się dać uniwersalnej podpowiedzi do ostatecznego problemu przy pisaniu pętli - Czy ja wiem, co ta pętla ma robić? Wiesz, że chcesz użyć pętli (inaczej zmarnowałeś czas czytając te, powoli pełznące do meritum, wprowadzenie - mam nadzieję, że jakoś pomogło), a więc chcesz jakiś fragment kodu zapętlić, i to skończoną ilość razy - gdyby chodziło o pętlę nieskończoną, to nie byłoby problemu, a skoro wiesz że ma być koniec, to zdefiniuj okoliczności tego końca. W formie warunku. "Moja pętla ma się powtarzać gdy..", "Moja pętla ma się przerwać gdy.." - jeśli potrafisz się wyrazić w języku naturalnym, to możesz to przełożyć jeden do jednego na warunek w C++. Jest "i", jest "lub", jest negacja i spora dowolność w tym, co można wpisać pomiędzy nie. Do tego trzeba zanegować wszystko razem, jeśli poszedłeś w kierunku "pętla ma się przerwać", a nie "pętla ma się powtarzać".

Ostatnia prosta

Warunek już jest, działa, ale wygląda tragicznie. Nie dałeś rady opisać go bez sporej ilości negacji. Teraz jest czas, żeby go trochę uprościć i przeredagować. Powiedzmy, że masz "i nie jest mniejsze od 10", co zapisałeś jako !(i < 10). Można to skrócić i wyrzucić negację, przez odwrócenie znaku porównania, "i jest większe równe 10" i >= 10. Często zdarza się błąd, że zapomina się o tym znaku równości. Bez niego, warunek inaczej się zachowa dla i równego 10 i do kodu dostanie się "błąd o jedynkę".
Teraz coś bardziej skomplikowanego: "pętla ma się przerwać, gdy albo i > 10, albo j == 0". Mówimy o przerwaniu pętli, więc warunek kontynuacji pętli będzie przeciwny:
C/C++
while( !( i > 10 || j == 0 ) )
Tu też można usunąć negację i warunek uprościć:
C/C++
while( i <= 10 && i != 0 )
A tu co się stało..? Mieliśmy 2 warunki, zajście dowolnego z nich zrywa pętlę: i > 10, j == 0. A więc, pętla wykonuje się, gdy żaden z nich nie zachodzi. A więc, "pętla powtarza się, gdy !(i > 10) i !(j == 0)". Nazywa się to prawem De Morgana.