Instrucțiunea for
Bucla pentru când știi de câte ori: cele trei părți ale antetului, pasul și greșelile de margine care schimbă rezultatul cu unu.
Anatomia
for (inițializare; condiție; pas) {
// corpul
}for (int i = 1; i <= 10; i++)
cout << i << " ";Ordinea de execuție: întâi inițializarea, o singură dată. Apoi condiție → corp → pas, iar și iar, până când condiția devine falsă.
Exact același lucru scris cu while:
int i = 1;
while (i <= 10) {
cout << i << " ";
i++;
}for nu poate face nimic în plus. Doar adună cele trei părți într-un singur loc, unde se văd împreună. De aceea îl preferi când știi dinainte numărul de pași.
Variante de antet
Descrescător, cu pas negativ:
for (int i = n; i >= 1; i--)
cout << i << " ";Din doi în doi:
for (int i = 2; i <= n; i += 2)
cout << i << " "; // numerele pare până la nCu doi contori:
for (int i = 0, j = n; i < j; i++, j--) {
// se apropie din ambele capete
}În pseudocod, pasul se scrie ca al treilea parametru: pentru i ← n,1,-1 execută.
Marginile
Aproape toate greșelile cu for sunt greșeli de margine: rezultatul iese exact cu unu mai mare sau mai mic.
for (int i = 1; i <= n; i++) // n treceri: 1, 2, …, n
for (int i = 0; i < n; i++) // n treceri: 0, 1, …, n-1
for (int i = 1; i < n; i++) // n-1 treceri
for (int i = 0; i <= n; i++) // n+1 treceriPe primele două le vei folosi. A doua este obligatorie când parcurgi un vector indexat de la 0.
Când nu ești sigur, verifică rapid: numără câte valori ia i, nu câte pare că sunt. De la a la b inclusiv sunt b - a + 1 valori, nu b - a.
Variabila contor
Declarată în antet, i există numai în interiorul buclei:
for (int i = 1; i <= n; i++) { ... }
cout << i; // eroare: i nu mai există aiciDacă îți trebuie valoarea și după, declar-o înainte:
int i;
for (i = 1; i <= n && v[i] != x; i++);
// aici i spune unde s-a oprit căutareaObservă ; de la sfârșitul liniei: bucla are corpul gol și totul se face în antet. E un tipar compact pentru căutări, dar folosește-l doar când chiar e mai clar așa.
Bucle imbricate
O buclă în interiorul alteia. Cea interioară se execută complet la fiecare trecere a celei exterioare.
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++)
cout << i * j << " ";
cout << "\n";
}Se afișează 3 linii a câte 4 numere, deci corpul interior se execută de 12 ori în total.
Regula de calcul: dacă exterioara face n pași și interioara m, corpul interior se execută n · m ori. Pentru n = m = 1000 sunt un milion de operații, și asta e în regulă. Pentru n = m = 100000 sunt zece miliarde, și asta e prea mult.
Folosește contori diferiți. Dacă bucla interioară folosește tot variabila i a buclei exterioare, fără să declare una nouă, o strică pe cea exterioară, iar programul se comportă foarte ciudat.
Un exemplu complet
Se citește un număr natural n. Să se afișeze toate perechile de numere naturale (a, b) cu a ≤ b și a + b = n.
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int a = 0; a <= n / 2; a++)
cout << a << " " << n - a << "\n";
return 0;
}Aici nu e nevoie de două bucle. b se calculează din a, iar condiția a ≤ b înseamnă exact a ≤ n/2. Cu o buclă în loc de două faci n/2 pași în loc de n². Problema e aceeași, dar ordinul de mărime e altul.
Reține ideea: înainte să pui a doua buclă, întreabă-te dacă a doua valoare nu se poate calcula din prima.
De reținut
- Antetul are trei părți. Ordinea de execuție este condiție → corp → pas.
- De la
alabinclusiv suntb - a + 1treceri. i < npentru vectori indexați de la 0,i <= npentru cei de la 1. Fii consecvent.- Buclele imbricate înmulțesc numărul de pași. Verifică ordinul de mărime.
- Dacă a doua valoare se deduce din prima, nu-ți trebuie a doua buclă.