Pętle
Komputer nie nudzi się powtarzaniem. Cztery rodzaje pętli wystarczą, żeby przejść przez tablicę, policzyć sumę tysiąca liczb, narysować choinkę ze znaków i pytać użytkownika tak długo, aż poda poprawne dane.
Czego się dziś nauczysz
- Napiszesz pętlę
forz licznikiem rosnącym, malejącym i o dowolnym kroku - Wybierzesz właściwą pętlę do zadania:
for,while,for…of - Przerwiesz pętlę przez
breaki pominiesz obieg przezcontinue - Zbudujesz pętlę zagnieżdżoną i wypiszesz nią tabliczkę mnożenia
- Rozpoznasz pętlę nieskończoną i wskażesz jej przyczynę
Pętla for
for ( let i = 0 ; i < 5 ; i++ ) {
│ │ │
│ │ └─ 3. co zrobić po każdym obiegu
│ └────────── 2. warunek: dopóki prawda, powtarzaj
└─────────────────────── 1. przygotowanie, wykonane raz na starcie
}
kolejność: 1 → 2 → blok → 3 → 2 → blok → 3 → 2 → … → 2 (fałsz) → koniec
// od 0 do 4
for (let i = 0; i < 5; i++) {
console.log(`Obieg numer ${i}`);
}
// od 1 do 10 — naturalniejsze przy liczeniu
for (let i = 1; i <= 10; i++) {
console.log(i);
}
// w dół
for (let i = 10; i >= 1; i--) {
console.log(i);
}
// co trzy
for (let i = 0; i <= 30; i += 3) {
console.log(i);
}
Zmienna sterująca (tradycyjnie i od indeks) istnieje tylko wewnątrz pętli.
Po jej zakończeniu znika — to ten sam zasięg blokowy, który poznałeś przy if.
Klasyczna pomyłka o jeden
Przy tablicy pięcioelementowej warunek i <= t.length daje sześć obiegów,
a ostatni sięga poza tablicę. Zapamiętaj parę: od zera do < length.
while i do…while
while powtarza blok, dopóki warunek jest prawdziwy. Używa się jej, gdy
nie wiadomo z góry, ile będzie obiegów:
// ile razy trzeba podzielić liczbę przez 2, żeby zejść do jedynki?
let liczba = 1000;
let kroki = 0;
while (liczba > 1) {
liczba = Math.floor(liczba / 2);
kroki++;
}
console.log(`Potrzeba ${kroki} kroków`); // 9
do…while różni się jednym: warunek sprawdzany jest po wykonaniu bloku,
więc blok wykona się zawsze co najmniej raz:
let proba = 0;
let wylosowana: number;
do {
wylosowana = Math.floor(Math.random() * 6) + 1;
proba++;
console.log(`Rzut ${proba}: ${wylosowana}`);
} while (wylosowana !== 6);
console.log(`Szóstka wypadła po ${proba} rzutach`);
Losowanie musi się odbyć, zanim można sprawdzić wynik — to typowa sytuacja dla
do…while. Ten sam schemat obsługuje pytanie użytkownika o dane: najpierw pytamy,
potem sprawdzamy, czy odpowiedź jest poprawna.
Pętla nieskończona
Pętla, której warunek nie stanie się fałszywy i która nie wykona break ani innego wyjścia, może działać bez końca. Przerwij taki program przez Ctrl+C. Sprawdź, co zapewnia postęp lub zakończenie; nie każda poprawna pętla musi zmieniać wartość bezpośrednio w warunku.
for…of i forEach
Gdy przechodzisz przez tablicę i nie potrzebujesz numeru pozycji, for…of jest
najczytelniejsza — nie ma w niej licznika, którym można się pomylić:
const przedmioty = ["INF.03", "INF.04", "matematyka"];
for (const p of przedmioty) {
console.log(p);
}
// gdy potrzebny jest też numer — entries() daje krotkę [indeks, wartość]
for (const [i, p] of przedmioty.entries()) {
console.log(`${i + 1}. ${p}`);
}
// forEach — ta sama praca, zapis funkcyjny
przedmioty.forEach((p, i) => {
console.log(`${i + 1}. ${p}`);
});
// for…of działa też na tekście — znak po znaku
for (const znak of "Lębork") {
console.log(znak);
}
W forEach nie działa break
forEach wywołuje funkcję dla każdego elementu i nie da się jej przerwać w połowie
(return pomija tylko jeden element, jak continue). Gdy potrzebujesz
wyjść wcześniej — użyj for…of.
Którą pętlę wybrać
| Pętla | Kiedy | Przykład zadania |
|---|---|---|
for | znasz liczbę obiegów lub potrzebujesz indeksu | wypisz liczby 1–100, przejdź tablicę wstecz, porównaj dwie tablice po indeksie |
for…of | przechodzisz elementy tablicy lub znaki tekstu | policz sumę ocen, znajdź najdłuższe słowo |
while | nie wiesz, ile obiegów będzie potrzeba | dziel, aż zejdziesz do jedynki; czytaj dane, aż się skończą |
do…while | blok musi wykonać się co najmniej raz | pytaj o dane, aż będą poprawne; losuj, aż wypadnie szóstka |
Praktyczna kolejność wyboru
Najpierw sprawdź, czy wystarczy for…of — jest najkrótsza i najbezpieczniejsza.
Jeśli potrzebujesz indeksu, sięgnij po for. while zostaw
do sytuacji naprawdę nieprzewidywalnych.
break i continue
const oceny = [5, 4, 1, 3, 5];
// break — przerywa całą pętlę
for (const ocena of oceny) {
if (ocena === 1) {
console.log("Znaleziono jedynkę — przerywam sprawdzanie");
break;
}
console.log(`Ocena ${ocena} w porządku`);
}
console.log("───");
// continue — pomija resztę bieżącego obiegu i idzie dalej
let suma = 0;
for (const ocena of oceny) {
if (ocena === 1) {
continue; // jedynek nie wliczamy
}
suma += ocena;
}
console.log(`Suma bez jedynek: ${suma}`); // 17
Ocena 5 w porządku
Ocena 4 w porządku
Znaleziono jedynkę — przerywam sprawdzanie
───
Suma bez jedynek: 17
Typowe zastosowanie break to wyszukiwanie: gdy znajdziesz to, czego szukasz,
nie ma powodu przeglądać reszty:
const uczniowie = ["Ala", "Bartek", "Cezary", "Dorota"];
const szukany = "Cezary";
let znalezionoNa = -1;
for (let i = 0; i < uczniowie.length; i++) {
if (uczniowie[i] === szukany) {
znalezionoNa = i;
break;
}
}
if (znalezionoNa === -1) {
console.log(`${szukany} nie występuje na liście`);
} else {
console.log(`${szukany} jest na pozycji ${znalezionoNa + 1}`);
}
Pętle zagnieżdżone
Pętla w pętli: dla każdego obiegu zewnętrznego wewnętrzna wykonuje się w całości. Przy dwóch pętlach po 10 obiegów daje to 100 wykonań bloku.
// nagłówek
let naglowek = " ";
for (let k = 1; k <= 10; k++) {
naglowek += String(k).padStart(4);
}
console.log(naglowek);
console.log("─".repeat(44));
// wiersze
for (let w = 1; w <= 10; w++) {
let wiersz = String(w).padStart(2) + " │";
for (let k = 1; k <= 10; k++) {
wiersz += String(w * k).padStart(4);
}
console.log(wiersz);
}
1 2 3 4 5 6 7 8 9 10
────────────────────────────────────────────
1 │ 1 2 3 4 5 6 7 8 9 10
2 │ 2 4 6 8 10 12 14 16 18 20
3 │ 3 6 9 12 15 18 21 24 27 30
4 │ 4 8 12 16 20 24 28 32 36 40
5 │ 5 10 15 20 25 30 35 40 45 50
6 │ 6 12 18 24 30 36 42 48 54 60
7 │ 7 14 21 28 35 42 49 56 63 70
8 │ 8 16 24 32 40 48 56 64 72 80
9 │ 9 18 27 36 45 54 63 72 81 90
10 │ 10 20 30 40 50 60 70 80 90 100
Rysowanie kształtów
const wysokosc = 6;
for (let i = 1; i <= wysokosc; i++) {
const spacje = " ".repeat(wysokosc - i);
const gwiazdki = "*".repeat(2 * i - 1);
console.log(spacje + gwiazdki);
}
console.log(" ".repeat(wysokosc - 1) + "|");
*
***
*****
*******
*********
***********
|
Dwa sposoby rysowania
Powyżej użyliśmy repeat, bo to krótsze. Na egzaminie i w zadaniach z innych języków
częściej rysuje się pętlą wewnętrzną: for (let j = 0; j < 2 * i - 1; j++) wiersz += "*";
Warto umieć oba — wynik jest identyczny.
break w pętli zagnieżdżonej
break przerywa tylko tę pętlę, w której stoi. Żeby wyjść z obu naraz, używa się
etykiety:
const plansza = [
[0, 0, 0],
[0, 9, 0],
[0, 0, 0]
];
szukanie:
for (let w = 0; w < plansza.length; w++) {
for (let k = 0; k < plansza[w].length; k++) {
if (plansza[w][k] === 9) {
console.log(`Znaleziono w wierszu ${w}, kolumnie ${k}`);
break szukanie; // wychodzi z OBU pętli
}
}
}
Etykiety używaj oszczędnie — zwykle lepszym rozwiązaniem jest przeniesienie szukania
do osobnej funkcji i wyjście przez return (lekcja 11).
Analiza frekwencji klasy
Program przetwarza dane obecności zapisane jako tekst, liczy statystyki dla każdego ucznia i dla całej klasy, wyszukuje uczniów zagrożonych i rysuje wykres frekwencji.
// o = obecny, n = nieobecny, s = spóźnienie
const dane = [
"Kowalska Marta:ooonooooosoo",
"Nowak Piotr:onnnooonnooo",
"Wiśniewski Jan:oooooooooooo",
"Dąbrowska Ewa:oonoosooonno"
];
const PROG_ZAGROZENIA = 75;
let sumaFrekwencji = 0;
let najlepszy = "";
let najlepszaFrekwencja = -1;
const zagrozeni: string[] = [];
console.log("UCZEŃ".padEnd(20) + "OB. NB. SP. FREK. WYKRES");
console.log("═".repeat(62));
for (const wiersz of dane) {
// ── rozbicie wiersza ──
const [nazwisko, obecnosci] = wiersz.split(":");
// ── zliczanie znak po znaku ──
let obecny = 0;
let nieobecny = 0;
let spoznienie = 0;
for (const znak of obecnosci) {
if (znak === "o") {
obecny++;
} else if (znak === "n") {
nieobecny++;
} else if (znak === "s") {
spoznienie++;
} else {
continue; // nieznany znak pomijamy
}
}
// ── obliczenia ──
const wszystkie = obecnosci.length;
const frekwencja = ((obecny + spoznienie) / wszystkie) * 100;
sumaFrekwencji += frekwencja;
// ── wiersz raportu ──
const pasek = "█".repeat(Math.round(frekwencja / 10));
console.log(
nazwisko.padEnd(20) +
String(obecny).padStart(3) +
String(nieobecny).padStart(5) +
String(spoznienie).padStart(5) +
(frekwencja.toFixed(1) + "%").padStart(8) + " " +
pasek
);
// ── wyszukiwanie skrajnych przypadków ──
if (frekwencja > najlepszaFrekwencja) {
najlepszaFrekwencja = frekwencja;
najlepszy = nazwisko;
}
if (frekwencja < PROG_ZAGROZENIA) {
zagrozeni.push(nazwisko);
}
}
// ── podsumowanie klasy ──
console.log("═".repeat(62));
console.log(`Średnia frekwencja klasy: ${(sumaFrekwencji / dane.length).toFixed(1)}%`);
console.log(`Najwyższa frekwencja: ${najlepszy} (${najlepszaFrekwencja.toFixed(1)}%)`);
if (zagrozeni.length === 0) {
console.log("Brak uczniów poniżej progu.");
} else {
console.log(`Poniżej ${PROG_ZAGROZENIA}%: ${zagrozeni.join(", ")}`);
}
UCZEŃ OB. NB. SP. FREK. WYKRES
══════════════════════════════════════════════════════════════
Kowalska Marta 10 1 1 91.7% █████████
Nowak Piotr 7 5 0 58.3% ██████
Wiśniewski Jan 12 0 0 100.0% ██████████
Dąbrowska Ewa 8 3 1 75.0% ████████
══════════════════════════════════════════════════════════════
Średnia frekwencja klasy: 81.3%
Najwyższa frekwencja: Wiśniewski Jan (100.0%)
Poniżej 75%: Nowak Piotr
Cztery wzorce w jednym programie
| Wzorzec | Gdzie i jak |
|---|---|
| Pętla w pętli | Zewnętrzna idzie po uczniach, wewnętrzna po znakach obecności. Klasyczne przetwarzanie danych dwuwymiarowych. |
| Akumulator | sumaFrekwencji i liczniki obecny, nieobecny —
zmienna deklarowana przed pętlą, powiększana w środku, odczytywana po. |
| Szukanie maksimum | najlepszaFrekwencja startuje od -1, żeby pierwszy uczeń
na pewno ją przebił. Razem z nazwiskiem zapisujemy dwie wartości naraz. |
| Zbieranie do tablicy | zagrozeni.push(nazwisko) — budowanie listy wyników wewnątrz pętli,
a potem sprawdzenie length, żeby wiedzieć, czy coś w niej jest. |
Sprawdź to sam
Dopisz do tablicy piątego ucznia z frekwencją poniżej progu i zobacz, jak zmienia się
podsumowanie. Potem zamień wewnętrzną drabinkę if / else if na instrukcję
switch — to dobre ćwiczenie z poprzedniej lekcji.
Na co uważać
| Kod | Problem |
|---|---|
for (let i = 0; i <= t.length; i++) |
Jeden obieg za dużo — ostatni element to undefined. |
while (i < 10) { console.log(i); } |
Brak i++ — pętla nieskończona. |
for (let i = 0; i < 5; i++); |
Średnik po nawiasie: pętla kręci się pusto, a blok wykona się raz. |
Modyfikowanie tablicy w trakcie for…of |
push lub splice podczas przechodzenia potrafi pominąć elementy
albo zapętlić program. Zbieraj wyniki do osobnej tablicy. |
let suma = 0; wewnątrz pętli |
Zerowanie przy każdym obiegu — akumulator deklaruj przed pętlą. |
break w forEach |
Błąd składni. forEach nie da się przerwać; użyj for…of. |
for (const x in tablica) |
Dostajesz indeksy jako tekst, nie wartości. Przy tablicach używaj of. |
Zadania
let s = "";
for (let i = 1; i <= 5; i++) {
if (i === 3) continue;
if (i === 5) break;
s += i;
}
console.log(s);
let n = 0;
let licznik = 0;
while (n < 20) {
n += 7;
licznik++;
}
console.log(`${n} ${licznik}`);
pokaż rozwiązanie
124
21 3
Pierwsza pętla: 1 i 2 dopisane, 3 pominięte przez continue, 4 dopisane,
przy 5 break kończy pętlę. Druga: 7, 14, 21 — po trzecim dodaniu warunek
n < 20 jest fałszywy, ale wartość została już powiększona.
Wypisz liczby 1–50, zastępując wielokrotności 3 przez Fizz, 5 przez Buzz, a 15 przez FizzBuzz. To popularne ćwiczenie kolejności warunków.
pokaż rozwiązanie
for (let i = 1; i <= 50; i++) {
if (i % 15 === 0) {
console.log("FizzBuzz");
} else if (i % 3 === 0) {
console.log("Fizz");
} else if (i % 5 === 0) {
console.log("Buzz");
} else {
console.log(i);
}
}
Kluczowa jest kolejność: warunek podzielności przez 15 musi być pierwszy, bo liczba podzielna przez 15 jest też podzielna przez 3 i zatrzymałaby się na tamtej gałęzi. To dokładnie problem kolejności warunków z lekcji 06.
Mając tablicę 15 losowych liczb (Math.floor(Math.random() * 100) w pętli),
policz bez używania Math.min, Math.max i metod tablicowych:
sumę, średnią, wartość najmniejszą i największą, liczbę elementów parzystych oraz
ile elementów jest powyżej średniej. Ostatnie wymaga dwóch przejść przez tablicę —
zastanów się dlaczego.
pokaż wskazówki
Średnią znasz dopiero po zsumowaniu wszystkich elementów, więc porównanie z nią musi odbyć się w drugiej pętli. To bardzo częsty schemat: pierwsze przejście liczy statystykę, drugie z niej korzysta.
Wypisz wszystkie liczby pierwsze z przedziału od 2 do 100. Liczba jest pierwsza, gdy dzieli się
bez reszty tylko przez 1 i samą siebie. Użyj pętli zagnieżdżonej i przerwij wewnętrzną przez
break, gdy tylko znajdziesz dzielnik. Policz też, ile ich jest.
pokaż rozwiązanie
let ile = 0;
let wiersz = "";
for (let n = 2; n <= 100; n++) {
let pierwsza = true;
for (let d = 2; d * d <= n; d++) {
if (n % d === 0) {
pierwsza = false;
break;
}
}
if (pierwsza) {
wiersz += String(n).padStart(4);
ile++;
if (ile % 10 === 0) {
console.log(wiersz);
wiersz = "";
}
}
}
if (wiersz !== "") console.log(wiersz);
console.log(`Znaleziono ${ile} liczb pierwszych`);
Warunek d * d <= n zamiast d < n to ważna optymalizacja:
wystarczy sprawdzać dzielniki do pierwiastka z liczby, bo większy dzielnik zawsze ma
mniejszego partnera. Przy stu liczbach różnicy nie widać, przy milionie — ogromną.
Program losuje liczbę od 1 do 100, a następnie sam ją odgaduje metodą połowienia przedziału: zaczyna od środka, a po każdej próbie zawęża zakres w zależności od tego, czy strzał był za wysoki, czy za niski. Wypisz każdą próbę i licznik prób. Ile najwyżej prób potrzeba? Sprawdź eksperymentalnie, uruchamiając program kilkanaście razy.
pokaż rozwiązanie
const szukana = Math.floor(Math.random() * 100) + 1;
let dol = 1;
let gora = 100;
let proby = 0;
let strzal: number;
do {
strzal = Math.floor((dol + gora) / 2);
proby++;
if (strzal < szukana) {
console.log(`Próba ${proby}: ${strzal} — za mało`);
dol = strzal + 1;
} else if (strzal > szukana) {
console.log(`Próba ${proby}: ${strzal} — za dużo`);
gora = strzal - 1;
}
} while (strzal !== szukana);
console.log(`Trafiono: ${szukana} w ${proby} próbach`);
Najwyżej siedem prób — bo każde połowienie zmniejsza przedział dwukrotnie, a 2⁷ = 128 > 100. To algorytm wyszukiwania binarnego, jeden z najważniejszych w informatyce; wrócimy do niego przy sortowaniu.
Co trzeba zapamiętać
forgdy znasz liczbę obiegów lub potrzebujesz indeksu; waruneki < length.for…ofto domyślny wybór przy tablicach i tekstach.whilegdy liczba obiegów zależy od danych;do…whilegdy blok musi wykonać się raz.- Każda pętla
whilemusi mieć instrukcję zbliżającą warunek do fałszu. breakprzerywa pętlę,continuepomija jeden obieg; w zagnieżdżonych działają tylko na swoim poziomie.- Akumulatory i liczniki deklaruj przed pętlą, nie w środku.
- Nie modyfikuj tablicy, po której właśnie przechodzisz.
W następnej lekcji
Zaczynamy blok o funkcjach. Cały kod, który do tej pory pisałeś w jednym ciągu, da się zamknąć w nazwane, wielokrotnie używane bloki z określonymi typami wejścia i wyjścia.