Pętle while i do-while
Instrukcja warunkowa pozwala coś pominąć. Pętla pozwala coś powtórzyć — tyle razy, ile trzeba, i tyle razy, ile z góry nie wiadomo. To dzięki niej program może pytać o dane do skutku, liczyć sumę wprowadzanych liczb albo działać, dopóki użytkownik nie wybierze „koniec”.
Czego się dziś nauczysz
- Powtórzysz fragment kodu, dopóki warunek jest spełniony
- Napiszesz pętlę, która pyta o dane tak długo, aż użytkownik wpisze poprawną wartość
- Odróżnisz
whileoddo-whilei wybierzesz właściwą - Zsumujesz i policzysz nieznaną z góry liczbę wprowadzonych wartości
- Rozpoznasz i naprawisz pętlę nieskończoną
Pętla while
Pętla while wygląda dokładnie jak if — z jedną różnicą: po wykonaniu bloku wraca i sprawdza warunek jeszcze raz. I jeszcze raz. Dopóki warunek jest prawdziwy.
while (warunek)
{
// instrukcje powtarzane, dopoki warunek jest prawdziwy
}
int i = 1;
while (i <= 5)
{
Console.WriteLine($"Powtorzenie numer {i}");
i++;
}
Console.WriteLine("Koniec petli.");
Powtorzenie numer 1
Powtorzenie numer 2
Powtorzenie numer 3
Powtorzenie numer 4
Powtorzenie numer 5
Koniec petli.
Co się dzieje krok po kroku
| Krok | Wartość i | Warunek i <= 5 | Działanie |
|---|---|---|---|
| 1 | 1 | prawda | wypisuje „1″, zwiększa i do 2 |
| 2 | 2 | prawda | wypisuje „2″, zwiększa i do 3 |
| 3 | 3 | prawda | wypisuje „3″, zwiększa i do 4 |
| 4 | 4 | prawda | wypisuje „4″, zwiększa i do 5 |
| 5 | 5 | prawda | wypisuje „5″, zwiększa i do 6 |
| 6 | 6 | fałsz | pętla się kończy, program idzie dalej |
Warunek sprawdzany jest przed każdym obiegiem
Także przed pierwszym. Gdyby i startowało od 10, warunek 10 <= 5 byłby od razu fałszywy i blok nie wykonałby się ani razu. To ważna cecha while — potrafi wykonać się zero razy.
Trzy rzeczy, o których trzeba pamiętać
Każda poprawna pętla while ma trzy elementy. Brak któregokolwiek to gotowy błąd:
int i = 1; // 1. przygotowanie - wartosc poczatkowa
while (i <= 5) // 2. warunek - kiedy powtarzac
{
Console.WriteLine(i);
i++; // 3. zmiana - to, co w koncu unieważni warunek
}
| Element | Gdy go brakuje | Objaw |
|---|---|---|
| przygotowanie | zmienna nie istnieje albo nie ma wartości | błąd kompilacji CS0103 lub CS0165 |
| warunek | warunek jest od początku fałszywy | pętla nie wykonuje się ani razu — łatwo tego nie zauważyć |
| zmiana | nic nie zmienia wartości sprawdzanej w warunku | pętla nieskończona — program się zawiesza |
Zmiana musi dotyczyć zmiennej z warunku
To nie wystarczy, że w pętli „coś się dzieje”. Musi zmieniać się dokładnie ta wartość, którą sprawdza warunek. Zwiększanie zmiennej suma, gdy warunek patrzy na i, nie zakończy pętli nigdy.
Pętla nieskończona
Prędzej czy później napiszesz taką pętlę przez pomyłkę. Warto wiedzieć, jak wygląda i co wtedy zrobić.
// 1. brak zmiany licznika
int i = 1;
while (i <= 5)
{
Console.WriteLine(i); // i nigdy nie rosnie
}
// 2. srednik po warunku
int j = 1;
while (j <= 5); // pusta instrukcja - petla bez tresci
{
Console.WriteLine(j);
j++;
}
// 3. zmiana w zla strone
int k = 5;
while (k > 0)
{
Console.WriteLine(k);
k++; // rosnie, a warunek czeka na zejscie do zera
}
Jak przerwać zawieszony program
W oknie konsoli naciśnij Ctrl + C. W Visual Studio zatrzymaj debugowanie kwadratowym przyciskiem „Stop” (albo Shift + F5). Jeśli program wypisuje coś w pętli, konsola potrafi zapchać się tysiącami linii w sekundę — im szybciej zareagujesz, tym lepiej.
Pętla nieskończona bywa celowa
Program z menu ma działać, dopóki użytkownik nie wybierze wyjścia. Wtedy pisze się pętlę bez warunku końcowego i przerywa ją instrukcją break — poznasz ją dokładniej w lekcji 16:
while (true)
{
Console.Write("Polecenie (koniec = zamknij): ");
string polecenie = (Console.ReadLine() ?? "").Trim().ToLower();
if (polecenie == "koniec")
{
break; // jedyne wyjscie z tej petli
}
Console.WriteLine($"Wykonuje: {polecenie}");
}
Taka pętla jest bezpieczna, o ile widzisz instrukcję break i jesteś pewien, że da się do niej dojść. Jeśli warunek wyjścia da się zapisać wprost w nawiasie while, zrób to — czyta się to lepiej.
Pętla do-while
Różnica jest jedna, ale zmienia wszystko: warunek sprawdzany jest po wykonaniu bloku. Dzięki temu blok wykona się co najmniej raz, choćby warunek był od początku fałszywy.
do
{
// instrukcje - wykonaja sie przynajmniej raz
}
while (warunek); // UWAGA: sredniki na koncu
int i = 10;
Console.WriteLine("--- while ---");
while (i <= 5)
{
Console.WriteLine($"while: {i}");
i++;
}
int j = 10;
Console.WriteLine("--- do-while ---");
do
{
Console.WriteLine($"do-while: {j}");
j++;
}
while (j <= 5);
--- while ---
--- do-while ---
do-while: 10
Pętla while nie wykonała się ani razu, bo warunek 10 <= 5 jest fałszywy od początku. Pętla do-while zdążyła wykonać się raz, zanim sprawdziła ten sam warunek.
| Cecha | while | do-while |
|---|---|---|
| Warunek sprawdzany | przed każdym obiegiem | po każdym obiegu |
| Minimalna liczba obiegów | zero | jeden |
| Średnik na końcu | nie ma | jest, po nawiasie warunku |
| Typowe zastosowanie | przetwarzanie danych, których może nie być | menu, pytanie o dane — coś, co trzeba zrobić raz, zanim jest o co pytać |
Jak wybrać
Zadaj sobie pytanie: czy jest sens wykonać ten blok choć raz, zanim cokolwiek sprawdzę? Przy pytaniu użytkownika o dane — tak, przecież najpierw trzeba zapytać. To do-while. Przy „wypisz pozostałe pozycje z listy” — niekoniecznie, lista może być pusta. To while.
Wzorzec: pytanie do skutku
To obiecany w lekcji 10 brakujący fragment. Do tej pory program przy błędnych danych po prostu się kończył. Teraz będzie pytał tak długo, aż dostanie to, czego potrzebuje.
int wiek;
bool poprawny;
do
{
Console.Write("Podaj wiek (0-120): ");
bool ok = int.TryParse(Console.ReadLine(), out wiek);
poprawny = ok && wiek >= 0 && wiek <= 120;
if (!poprawny)
{
Console.WriteLine(" To nie jest poprawny wiek. Sprobuj jeszcze raz.");
}
}
while (!poprawny);
Console.WriteLine($"Dziekuje. Zapisany wiek: {wiek}");
Podaj wiek (0-120): dwadziescia
To nie jest poprawny wiek. Sprobuj jeszcze raz.
Podaj wiek (0-120): -5
To nie jest poprawny wiek. Sprobuj jeszcze raz.
Podaj wiek (0-120): 17
Dziekuje. Zapisany wiek: 17
Dlaczego akurat tak
| Fragment | Uzasadnienie |
|---|---|
int wiek; przed pętlą |
Zmienna musi przeżyć zakończenie pętli, bo używamy jej w ostatniej linii. Zadeklarowana wewnątrz bloku znikłaby razem z klamrą. |
out wiek, a nie out int wiek |
Zmienna już istnieje, więc nie deklarujemy jej ponownie. Zapis out int wiek wewnątrz pętli utworzyłby drugą, lokalną zmienną i pierwsza zostałaby pusta. |
do-while, a nie while |
Zanim cokolwiek sprawdzimy, musimy najpierw zapytać. Wersja z while wymagałaby sztucznego ustawienia poprawny = false przed pętlą. |
ok && wiek >= 0 && ... |
Skrócone obliczanie z lekcji 8: gdy ok jest fałszywe, reszta warunku nie jest nawet sprawdzana. To ważne, bo po nieudanej konwersji wiek zawiera zero, które przypadkiem mieści się w zakresie. |
while (!poprawny); |
„Powtarzaj, dopóki nie jest poprawnie”. Zapis while (poprawny) byłby odwrotnością — pętla kręciłaby się tylko przy dobrych danych. |
Zawsze daj drogę wyjścia
Pętla walidacji zapętla się, dopóki dane są złe. Jeśli program dostaje dane z pliku, który się skończył, Console.ReadLine() zwraca brak wartości — i pętla nigdy się nie skończy. W programach uruchamianych z klawiatury to nie problem, ale w poważniejszym kodzie warto ograniczyć liczbę prób albo pozwolić na wpisanie „anuluj”.
Wzorzec: licznik i sumator
Drugi wzorzec, który wraca w co drugim zadaniu: przed pętlą przygotowujemy zmienne na wynik, w pętli je uzupełniamy, po pętli wypisujemy.
int suma = 0; // sumator zaczyna od ZERA
int ile = 0; // licznik zaczyna od ZERA
int i = 1;
while (i <= 10)
{
suma += i; // to samo co: suma = suma + i
ile++;
i++;
}
double srednia = (double)suma / ile;
Console.WriteLine($"Suma liczb 1-10: {suma}");
Console.WriteLine($"Ile skladnikow: {ile}");
Console.WriteLine($"Srednia: {srednia:F2}");
Suma liczb 1-10: 55
Ile skladnikow: 10
Srednia: 5,50
Sumator zaczyna od zera, iloczyn od jedynki
Gdyby suma startowała od 1, wynik byłby zawyżony o jeden. Odwrotnie przy mnożeniu: zmienna na iloczyn musi zaczynać od 1, bo wszystko pomnożone przez zero daje zero. To najczęstszy błąd przy liczeniu silni.
Skąd (double) przed dzieleniem
suma i ile są typu int, więc suma / ile byłoby dzieleniem całkowitym — dla 55 i 10 dałoby 5, a nie 5,5. Rzutowanie jednej ze stron na double włącza dzielenie z resztą. To pułapka z lekcji 7 i wraca przy każdej średniej.
Wzorzec: wartownik
Wartownik (ang. sentinel) to umówiona wartość, która oznacza koniec danych. Użytkownik wprowadza liczby jedna po drugiej, a wpisanie zera (albo pustej linii, albo słowa „koniec”) kończy zbieranie.
Console.WriteLine("Podawaj liczby. Pusta linia konczy.");
double suma = 0;
int ile = 0;
Console.Write("Liczba: ");
string wpis = Console.ReadLine() ?? "";
while (wpis.Length > 0)
{
if (double.TryParse(wpis, out double liczba))
{
suma += liczba;
ile++;
}
else
{
Console.WriteLine(" Pomijam - to nie liczba.");
}
Console.Write("Liczba: ");
wpis = Console.ReadLine() ?? "";
}
Console.WriteLine();
if (ile == 0)
{
Console.WriteLine("Nie podano zadnej liczby.");
}
else
{
Console.WriteLine($"Podano liczb: {ile}");
Console.WriteLine($"Suma: {suma:F2}");
Console.WriteLine($"Srednia: {suma / ile:F2}");
}
Podawaj liczby. Pusta linia konczy.
Liczba: 12,5
Liczba: 7
Liczba: abc
Pomijam - to nie liczba.
Liczba: 20,5
Liczba:
Podano liczb: 3
Suma: 40,00
Srednia: 13,33
Wczytanie pojawia się dwa razy — i to jest poprawne
Raz przed pętlą (żeby warunek miał co sprawdzić przy pierwszym obiegu) i raz na końcu bloku (żeby przy kolejnym sprawdzeniu warunek patrzył na nową wartość). Ten układ nazywa się „wczytaj z wyprzedzeniem”. Alternatywa to while (true) z instrukcją break w środku — poznasz ją w lekcji 16 i wtedy powtórzenie zniknie.
Licznik dzieli — sprawdź, czy nie jest zerem
Gdyby użytkownik od razu nacisnął Enter, ile zostałoby zerem, a suma / ile na typie double dałoby NaN — program nie zgłosiłby błędu, tylko wypisał bzdurę. Dlatego przed liczeniem średniej zawsze sprawdzamy licznik.
Użyte w tej lekcji elementy
| Nazwa | Zwraca | Działanie |
|---|---|---|
i++ / i-- | liczba | Zwiększa / zmniejsza zmienną o 1. W pętli while to najczęstsza „zmiana”, która doprowadza do zakończenia. |
suma += x | liczba | Skrót od suma = suma + x. Analogicznie -=, *=, /=. |
s.Length | int |
Właściwość — liczba znaków napisu. s.Length > 0 to sprawdzenie „coś wpisano”. Uwaga: to nie metoda, więc bez nawiasów. |
double.TryParse(s, out x) | bool |
Znane z lekcji 9. Wywołane wprost w warunku if pozwala połączyć konwersję i sprawdzenie w jedną linię. |
Console.ReadKey(true) | ConsoleKeyInfo |
Czeka na jeden klawisz. Właściwość .Key daje wciśnięty klawisz, a .KeyChar — jego znak. Przydatne do menu bez Entera. |
Math.Abs(x) | liczba | Wartość bezwzględna — w tej lekcji przy sprawdzaniu, czy liczba zmieściła się w tolerancji. |
new string(znak, n) | string |
Napis złożony z n powtórzeń znaku — wygodne do rysowania linii i słupków. |
Kasa sklepowa
Program przyjmuje kolejne pozycje zakupów (cena i ilość), sumuje je, a na koniec przyjmuje zapłatę i wydaje resztę. Wszystkie trzy wzorce z tej lekcji naraz.
Console.WriteLine("=== KASA ===");
Console.WriteLine("Podaj cene pozycji. Zero konczy paragon.");
Console.WriteLine();
decimal suma = 0;
int pozycje = 0;
// --- 1. wartownik: zbieranie pozycji do wpisania zera ---
decimal cena;
do
{
Console.Write($"Pozycja {pozycje + 1} - cena (0 = koniec): ");
bool ok = decimal.TryParse(Console.ReadLine(), out cena);
if (!ok || cena < 0)
{
Console.WriteLine(" Podaj nieujemna liczbe.");
cena = -1; // wymuszamy kolejny obieg
}
else if (cena > 0)
{
suma += cena;
pozycje++;
Console.WriteLine($" Dodano. Suma: {suma:F2} zl");
}
}
while (cena != 0);
Console.WriteLine();
if (pozycje == 0)
{
Console.WriteLine("Paragon pusty. Do widzenia.");
}
else
{
Console.WriteLine(new string('-', 28));
Console.WriteLine($"{"Pozycji:",-16}{pozycje,12}");
Console.WriteLine($"{"DO ZAPLATY:",-16}{suma,12:F2}");
Console.WriteLine(new string('-', 28));
Console.WriteLine();
// --- 2. walidacja: zaplata musi wystarczyc ---
decimal zaplata;
bool wystarczy;
do
{
Console.Write("Otrzymano od klienta: ");
bool ok = decimal.TryParse(Console.ReadLine(), out zaplata);
wystarczy = ok && zaplata >= suma;
if (!ok)
{
Console.WriteLine(" To nie jest kwota.");
}
else if (!wystarczy)
{
Console.WriteLine($" Za malo o {suma - zaplata:F2} zl.");
}
}
while (!wystarczy);
Console.WriteLine();
Console.WriteLine($"Reszta: {zaplata - suma:F2} zl");
}
=== KASA ===
Podaj cene pozycji. Zero konczy paragon.
Pozycja 1 - cena (0 = koniec): 12,50
Dodano. Suma: 12,50 zl
Pozycja 2 - cena (0 = koniec): 4,99
Dodano. Suma: 17,49 zl
Pozycja 3 - cena (0 = koniec): 23,00
Dodano. Suma: 40,49 zl
Pozycja 4 - cena (0 = koniec): 0
----------------------------
Pozycji: 3
DO ZAPLATY: 40,49
----------------------------
Otrzymano od klienta: 20
Za malo o 20,49 zl.
Otrzymano od klienta: 50
Reszta: 9,51 zl
Omówienie
| Fragment | Co robi i dlaczego tak |
|---|---|
decimal cena; przed pętlą |
Warunek while (cena != 0) stoi poza blokiem, więc zmienna musi być zadeklarowana wcześniej. Deklaracja wewnątrz pętli dałaby CS0103. |
cena = -1; po błędzie |
Sztuczka, która wygląda dziwnie, ale rozwiązuje realny problem: po nieudanym TryParse zmienna zawiera zero, a zero jest sygnałem końca. Bez tej linii wpisanie litery kończyłoby paragon. Ustawiamy wartość, która na pewno nie jest zerem — pętla robi kolejny obieg. |
else if (cena > 0) |
Zero nie jest ani błędem, ani pozycją — to sygnał końca. Dlatego dodajemy do sumy tylko wartości dodatnie, a wyjście z pętli obsługuje sam warunek. |
{pozycje + 1} w zachęcie |
Licznik pokazuje numer pozycji, którą użytkownik właśnie wpisuje, a nie liczbę już dodanych. Drobiazg, ale bez tego pierwsza pozycja miałaby numer 0. |
while (!wystarczy) |
Druga pętla, ta sama konstrukcja co wzorzec walidacji. Warunek łączy dwa wymagania: kwota ma być liczbą i ma wystarczyć. |
suma - zaplata w komunikacie |
Konkretna informacja, ile brakuje, zamiast ogólnego „za mało”. Przy danych 40,49 i 20,00 daje 20,49. |
decimal, nie double |
Pieniądze. Przy double suma 12,50 + 4,99 + 23,00 mogłaby wyjść 40,489999999999995 i reszta różniłaby się o grosz. |
Sprawdź sam
12,50 + 4,99 + 23,00 = 40,49. Zapłata 50,00 zł daje resztę 9,51 zł. Uruchom program i wpisz swoje ceny — potem policz na kartce i porównaj.
Na czym się najczęściej potykamy
| Błąd | Co się dzieje i jak poprawić |
|---|---|
| brak zmiany licznika | Pętla nieskończona — program się zawiesza. Sprawdź, czy w bloku zmienia się dokładnie ta zmienna, którą bada warunek. |
while (warunek); |
Średnik tworzy pustą instrukcję: pętla bez treści, czyli zawieszenie. Kompilator ostrzega (CS0642), ale program się zbuduje. Wyjątek: w do-while średnik po while (...) jest obowiązkowy. |
brak średnika w do-while |
Odwrotna pomyłka — CS1002. Zapamiętaj: while bez średnika, do-while ze średnikiem. |
out int wiek wewnątrz pętli |
Tworzy nową zmienną w każdym obiegu, a ta zadeklarowana przed pętlą zostaje pusta. Gdy zmienna już istnieje, pisz samo out wiek. |
| zmienna wyniku deklarowana w pętli | Zerowanie sumy przy każdym obiegu — na końcu zostaje tylko ostatnia wartość. Sumator i licznik deklaruj przed pętlą. |
| iloczyn zaczynający od zera | Wynik zawsze zero. Zmienna na iloczyn startuje od 1. |
suma / ile na typach int |
Dzielenie całkowite obcina resztę: 55/10 daje 5. Rzutuj jedną stronę: (double)suma / ile. |
| dzielenie przez licznik równy zero | Przy typie int program przerywa działanie (DivideByZeroException), przy double po cichu zwraca NaN. Zawsze sprawdź if (ile > 0). |
zero jako wartownik przy TryParse |
Po nieudanej konwersji zmienna dostaje zero — czyli przypadkiem sygnał końca. Albo wybierz inny wartownik (pusta linia), albo po błędzie nadpisz zmienną wartością, która końca nie oznacza. |
| błąd o jeden | i < 5 daje pięć obiegów (1–4 przy starcie od 1), i <= 5 daje pięć obiegów przy starcie od 1. Zanim uruchomisz, wypisz sobie pierwszy i ostatni obieg na kartce. |
Zadania
Wczytaj liczbę całkowitą dodatnią i odlicz od niej w dół do 1, a na końcu wypisz „Start!”. Każdą liczbę w osobnej linii.
pokaż rozwiązanie
Console.Write("Od ilu odliczac? ");
bool ok = int.TryParse(Console.ReadLine(), out int start);
if (!ok || start < 1)
{
Console.WriteLine("Podaj liczbe calkowita dodatnia.");
}
else
{
int i = start;
while (i >= 1)
{
Console.WriteLine(i);
i--;
}
Console.WriteLine("Start!");
}
Od ilu odliczac? 5
5
4
3
2
1
Start!
Licznik idzie w dół, więc zmianą jest i--, a warunkiem i >= 1. Pomyłka i++ dałaby pętlę nieskończoną — warunek i >= 1 nigdy przestałby być prawdziwy.
Kopiujemy start do i, żeby zachować wartość wpisaną przez użytkownika. Pętla mogłaby modyfikować start bezpośrednio, ale wtedy po jej zakończeniu nie dałoby się już powiedzieć, od czego zaczynaliśmy.
Wczytaj liczbę od 1 do 10 i wypisz jej tabliczkę mnożenia od 1 do 10, w równych kolumnach.
pokaż rozwiązanie
Console.Write("Podaj liczbe (1-10): ");
bool ok = int.TryParse(Console.ReadLine(), out int n);
if (!ok || n < 1 || n > 10)
{
Console.WriteLine("Podaj liczbe calkowita z zakresu 1-10.");
}
else
{
Console.WriteLine();
int i = 1;
while (i <= 10)
{
Console.WriteLine($"{n,3} x {i,2} = {n * i,4}");
i++;
}
}
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
...
7 x 10 = 70
Wyrównanie z lekcji 11 sprawia, że znaki równości stoją w jednej kolumnie także wtedy, gdy wynik ma dwie albo trzy cyfry. Bez niego tabliczka rozjechałaby się przy przejściu z 9 na 10.
W następnej lekcji zapiszemy tę samą pętlę jako for — będzie o dwie linie krótsza.
Wczytaj liczbę całkowitą dodatnią i policz sumę jej cyfr — bez zamieniania jej na tekst. Wskazówka: reszta z dzielenia przez 10 daje ostatnią cyfrę, a dzielenie całkowite przez 10 ją odcina.
pokaż rozwiązanie
Console.Write("Podaj liczbe calkowita dodatnia: ");
bool ok = int.TryParse(Console.ReadLine(), out int liczba);
if (!ok || liczba < 0)
{
Console.WriteLine("Podaj liczbe calkowita nieujemna.");
}
else
{
int reszta = liczba;
int suma = 0;
int cyfry = 0;
do
{
int cyfra = reszta % 10; // ostatnia cyfra
suma += cyfra;
cyfry++;
reszta /= 10; // odcinamy ja
}
while (reszta > 0);
Console.WriteLine($"Liczba {liczba} ma {cyfry} cyfr, ich suma to {suma}.");
}
Podaj liczbe calkowita dodatnia: 48317
Liczba 48317 ma 5 cyfr, ich suma to 23.
Dlaczego do-while, a nie while? Dla liczby 0 pętla while (reszta > 0) nie wykonałaby się ani razu i program stwierdziłby, że zero ma zero cyfr. do-while zawsze przetworzy przynajmniej jedną cyfrę — i to jest poprawne, bo zero to jedna cyfra.
Pracujemy na kopii reszta, bo oryginał jest potrzebny do wypisania komunikatu. Pętla niszczy tę liczbę, na której działa.
Prześledź na kartce dla 48317: 48317 % 10 = 7, potem 4831 % 10 = 1, potem 483 % 10 = 3, 48 % 10 = 8, 4 % 10 = 4. Suma: 7 + 1 + 3 + 8 + 4 = 23.
Program losuje liczbę od 1 do 100, a użytkownik zgaduje. Po każdej próbie program podpowiada „za mało” albo „za dużo”. Na końcu wypisuje liczbę prób.
pokaż rozwiązanie
Random losowy = new Random();
int szukana = losowy.Next(1, 101); // 1 wlacznie, 101 WYLACZNIE
int proby = 0;
int strzal;
Console.WriteLine("Zgadnij liczbe od 1 do 100.");
do
{
Console.Write("Twoja propozycja: ");
bool ok = int.TryParse(Console.ReadLine(), out strzal);
if (!ok)
{
Console.WriteLine(" To nie jest liczba.");
strzal = -1; // zeby petla sie nie skonczyla
continue;
}
proby++;
if (strzal < szukana)
{
Console.WriteLine(" Za malo.");
}
else if (strzal > szukana)
{
Console.WriteLine(" Za duzo.");
}
}
while (strzal != szukana);
Console.WriteLine();
Console.WriteLine($"Trafiles! Liczba to {szukana}. Prob: {proby}.");
Zgadnij liczbe od 1 do 100.
Twoja propozycja: 50
Za malo.
Twoja propozycja: 75
Za duzo.
Twoja propozycja: 62
Za malo.
Twoja propozycja: 68
Za duzo.
Twoja propozycja: 65
Trafiles! Liczba to 65. Prob: 5.
new Random() tworzy generator liczb losowych, a Next(1, 101) zwraca liczbę od 1 do 100. Górna granica jest wyłączna — to bardzo częsta pomyłka, przez którą setka nigdy by nie wypadła.
Instrukcja continue przeskakuje do sprawdzenia warunku, pomijając resztę bloku — dzięki niej błędny wpis nie zwiększa licznika prób. Poznasz ją dokładniej w lekcji 16; tutaj potraktuj ją jako „wróć na początek pętli”.
Zwróć uwagę, że przy trafieniu żaden komunikat się nie wypisuje — obie gałęzie if dotyczą chybienia, a wygraną obsługuje warunek pętli.
Do samodzielnego przemyślenia: optymalna strategia to za każdym razem strzelać w środek pozostałego przedziału. Ile maksymalnie prób potrzeba dla zakresu 1–100?
Użytkownik podaje liczby, aż wpisze pustą linię. Wypisz: ile liczb podano, sumę, średnią, wartość najmniejszą i największą.
pokaż rozwiązanie
Console.WriteLine("Podawaj liczby. Pusta linia konczy.");
Console.WriteLine();
int ile = 0;
double suma = 0;
double najmniejsza = 0;
double najwieksza = 0;
Console.Write("Liczba: ");
string wpis = Console.ReadLine() ?? "";
while (wpis.Length > 0)
{
if (!double.TryParse(wpis, out double liczba))
{
Console.WriteLine(" Pomijam - to nie liczba.");
}
else
{
suma += liczba;
if (ile == 0)
{
// pierwsza liczba jest jednoczesnie najmniejsza i najwieksza
najmniejsza = liczba;
najwieksza = liczba;
}
else
{
if (liczba < najmniejsza) { najmniejsza = liczba; }
if (liczba > najwieksza) { najwieksza = liczba; }
}
ile++;
}
Console.Write("Liczba: ");
wpis = Console.ReadLine() ?? "";
}
Console.WriteLine();
if (ile == 0)
{
Console.WriteLine("Nie podano zadnej liczby.");
}
else
{
Console.WriteLine($"{"Ile liczb:",-14}{ile,10}");
Console.WriteLine($"{"Suma:",-14}{suma,10:F2}");
Console.WriteLine($"{"Srednia:",-14}{suma / ile,10:F2}");
Console.WriteLine($"{"Najmniejsza:",-14}{najmniejsza,10:F2}");
Console.WriteLine($"{"Najwieksza:",-14}{najwieksza,10:F2}");
}
Podawaj liczby. Pusta linia konczy.
Liczba: 12
Liczba: -3,5
Liczba: 40
Liczba: 7,25
Liczba:
Ile liczb: 4
Suma: 55,75
Srednia: 13,94
Najmniejsza: -3,50
Najwieksza: 40,00
Najważniejszy fragment to obsługa pierwszej liczby. Gdyby najmniejsza startowała od zera, to dla samych liczb dodatnich program zawsze zwracałby zero jako minimum. Rozwiązanie: pierwszą wczytaną wartość wpisujemy jednocześnie jako minimum i maksimum, a dopiero kolejne porównujemy.
Alternatywa spotykana w podręcznikach to start od double.MaxValue i double.MinValue. Działa, ale jest mniej czytelna i przy pustej liście zostawia dziwaczne wartości.
Sprawdzenie: 12 + (−3,5) + 40 + 7,25 = 55,75, średnia 55,75 / 4 = 13,9375 → po zaokrągleniu 13,94.
Wczytaj dwie liczby całkowite dodatnie i policz ich największy wspólny dzielnik algorytmem Euklidesa: dopóki druga liczba nie jest zerem, zastępuj parę (a, b) parą (b, a % b). Wypisz też najmniejszą wspólną wielokrotność.
pokaż rozwiązanie
Console.Write("Pierwsza liczba: ");
bool ok1 = int.TryParse(Console.ReadLine(), out int a);
Console.Write("Druga liczba: ");
bool ok2 = int.TryParse(Console.ReadLine(), out int b);
if (!ok1 || !ok2 || a < 1 || b < 1)
{
Console.WriteLine("Podaj dwie liczby calkowite dodatnie.");
}
else
{
int x = a;
int y = b;
int krok = 0;
Console.WriteLine();
Console.WriteLine($"{"krok",6}{"x",8}{"y",8}{"x % y",8}");
Console.WriteLine(new string('-', 30));
while (y != 0)
{
krok++;
Console.WriteLine($"{krok,6}{x,8}{y,8}{x % y,8}");
int pomocnicza = x % y;
x = y;
y = pomocnicza;
}
// po zakonczeniu petli NWD siedzi w zmiennej x
int nwd = x;
long nww = (long)a / nwd * b;
Console.WriteLine();
Console.WriteLine($"NWD({a}, {b}) = {nwd}");
Console.WriteLine($"NWW({a}, {b}) = {nww}");
}
Pierwsza liczba: 48
Druga liczba: 18
krok x y x % y
------------------------------
1 48 18 12
2 18 12 6
3 12 6 0
NWD(48, 18) = 6
NWW(48, 18) = 144
Zmienna pomocnicza jest konieczna. Zapis x = y; y = x % y; byłby błędny — po pierwszym przypisaniu x ma już nową wartość i reszta wyliczyłaby się z niewłaściwej pary. Zamiana wartości dwóch zmiennych zawsze wymaga trzeciej (albo krotki: (x, y) = (y, x % y); — to zapis z bloku o C# nowoczesnym).
Pracujemy na kopiach x i y, bo a i b są potrzebne w komunikacie końcowym i do wyliczenia NWW.
Uwaga na kolejność w NWW. Wzór to a · b / NWD, ale zapisany jako a / nwd * b — najpierw dzielimy, potem mnożymy. Dzięki temu iloczyn a · b nigdy nie powstaje i nie ma szans przepełnić zakresu. Dzielenie jest bezpieczne, bo NWD z definicji dzieli a bez reszty.
Tabelka z krokami nie jest częścią algorytmu — dopisałem ją, żebyś zobaczył, co dzieje się w każdym obiegu. W gotowym programie zostawiłbyś samą pętlę.
Co trzeba zapamiętać
whilesprawdza warunek przed blokiem — może wykonać się zero razy.do-whilesprawdza po — wykona się co najmniej raz i kończy się średnikiem.- Każda pętla potrzebuje trzech rzeczy: wartości początkowej, warunku i zmiany, która ten warunek w końcu unieważni.
- Brak zmiany albo średnik po
while (...)to pętla nieskończona. Przerywasz ją przez Ctrl + C. - Do pytania o dane „do skutku” używaj
do-whileze zmienną logicznąpoprawny. - Sumator i licznik deklaruj przed pętlą; suma zaczyna od 0, iloczyn od 1.
- Przed policzeniem średniej sprawdź, czy licznik nie jest zerem, i rzutuj na
double. - Przy szukaniu minimum i maksimum pierwszą wartość przyjmij za oba naraz — nie startuj od zera.
- Zero jako wartownik jest ryzykowne przy
TryParse, bo nieudana konwersja też daje zero.