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 n

Cu 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 treceri

Pe 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ă aici

Dacă îț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ăutarea

Observă ; 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 a la b inclusiv sunt b - a + 1 treceri.
  • i < n pentru vectori indexați de la 0, i <= n pentru 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ă.