Цикъла for

Вече се запознахте с циклите с предусловие и постусловие do и while. Днес ще научите за цикъла for. Това е може би най – удобния и използван от всички вид цикъл. Неговия запис ни позволява да спестим ред, два от програмния код. В същото време кода остава достатъчно експресивен.

Синтаксис

for( инициализация; условие; промяна ) {
    -- код който ще се изпълнява --
}

Ето и пример:

for(int i = 0; i <= 10; i++) {
    cout << i << endl;
}

Ето как работи:

  • Когато влезем в цикъла се изпълнява полето инициализация. Това се прави само веднъж, преди цикъла да започне. Тук става и декларацията и инициализацията на водещата променлива, ако не сте го направили;
  • Преди всяка итерация се проверява условието. Както в цикъла while. Итерация се прави само, ако условието е true.
  • В края на всяка итерация се изпълнява полето промяна. След което програмата се връща в горната точка и проверява условието. Започва следваща итерация. Или при false цикъла приключва.

В примера, който дадохме е очевидно че се отпечатват числата от 0 до 10 включително. Нашата водеща променлива i участва и в трите полета. Това ни спестява няколко реда програмен код, не мислите ли?

Понякога може да се пропусне някое от условията – инициализация, условие, промяна. Ако това е удачно. Но не трябва да се забравя знака ‘;’, който ги разделя.

// безкраен цикъл
for(;;) {
    ...
}

За какво да внимаваме

Честа грешка е двусмислието в полетата условие и промяна. Относно това което искате да направите. В нашия прост пример отпечатваме числа в нарастващ ред. Но помислете за следните случай?

// пример 1:
for(int i = 0; i < 10; i--)
    cout << i << endl;

// пример 2:
for(int i = 0; i > 10; i++)
    cout << i << endl;
  • Пример 1: Инициализираме променливата с 0. Проверката минава. Но декрементираме променливата. Условието винаги е true (променливата не нараства). И програмата изпада в безкраен цикъл;
  • Пример 2: Не се отпечатва нищо. Просто защото сме объркали знака на условието. И то винаги връща false.

Друго за което трябва да се внимава е да не излезете от границите на масив. Ако имате масив от 100 елемента индексите са от 0 до 99. Ако се опитате в цикъл да запишете нещо на индекс 100, това е недефинирано поведение. Може да презапишете друга променлива или да достъпите неразрешена област от паметта. Това би довело до срив на програмата ви в най – добрия случай. Или до грешни резултати несъвпадащи с очакванията ви.

int MAX = 100;
int data[MAX];

// грешка, пишем в data[100] !!!
for(int i = 0; i <= MAX; i++)
    data[i] = 0;  
    
// това е ОК, правилно
for(int i = 0; i < MAX; i++)
    data[i] = 0;

Range based for цикъл

Това е друг вариант на for цикъла. По безопасен и по удобен. Тук водещата променлива е скрита от програмиста. А цикъла обхожда целия масив или колекция от данни. Кода е по описателен. Този синтаксис е въведен от C++ 11.

int data[100];
for(int i: data) {
    cout << i << endl;
}

Горния пример безопасно ще отпечата масива. При всяка итерация стойността на елемент от масива се копира в променливата i и тогава се отпечатва. За прости типове това не е проблем. Но когато елементите на масива (колекцията) са големи, като класове или структури тогава се ползват референции. За референциите ще учим скоро. Тук само ще ги споменем във връзка с този вид цикъл.

Какво е референция?

Референция (&) е просто друго име (alias) за вече съществуваща променлива. Когато използвате референции не се извършва копиране на стойността на самата променлива. Това ни позволява директно да работим с елементите на масива. Референциите работят като указателите. В горния пример се копира стойността на data[n] в променливата i. Копира се самата стойност. Както казахме това е бавно при големи обекти. При референция се копира само адреса на клетката от масива. Което е цяло число. Това намалява товара и програмата работи по бързо.

#include <iostream>
using namespace std;

int main() {
    int data[10];
    
    // инициализация на масива
    for(int& i: data)
        i = -42;
    
    // извеждане
    for(const int& a: data)
        cout << a << endl;
}

Break, continue, return

Тези команди работят по същия начин както при останалите видове цикли. Имат ефект в тялото на цикъла в който са извикани.

Вложени цикли

Вече сме ги илюстрирали и знаете как работят. Тук ще ви покажем два практични примера.

Рисуване на триъгълник в термнала 🙂

#include <iostream>
using namespace std;

int main() {
    int n = 5;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            cout << "*";
        }
        cout << "\n";
    }
}

Сортиране на колекция от цели числа представена в масив

#include <iostream>
using namespace std;

int main() {
    int arr[] = {5, 42, 13, 10, 2, 0, 9, 1, 3, 32};
    
    // брой на елементите в масива
    int n = sizeof(arr) / sizeof(int);
    
    // извежда несортирания масив
    cout << endl;
    for(const int &x: arr)
        cout << x << ", ";
    cout << endl;
    
    // bubble sort
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // размяна
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    
    // извежда вече сортирания масив
    for(const int &x: arr)
        cout << x << ", ";
    cout << endl;
}

Задачи

1. Въведи N и намери сумата от 1 до N.

Решение
#include <iostream>
using namespace std;

int main() {
    int n, sum = 0;
    
    cout << "N = ";
    cin >> n;

    for (int i = 1; i <= n; i++) {
        sum += i;
    }

    cout << sum << endl;
}

2. Намери n! Факториел

Решение
#include <iostream>
using namespace std;

int main() {
    int n;
    long long fact = 1;
 
    cout << "N = ";
    cin >> n;

    for (int i = 1; i <= n; i++) {
        fact *= i;
    }

    cout << fact << endl;
}

3. Направете масив съдържа числата от 1 до 100. Отпечатайте го. Намерете сумата на всички елементи от масива. Използвайте range based for цикъл.

Pешение
#include <iostream>
using namespace std;

int main() {
    int arr[100];
    
    // инициализация
    int q = 1;
    for(int& k: arr) {
        k = q;
        q++;
    }
    
    // проверка и сумиране
    int sum = 0;
    for(const int& k: arr) {
        cout << k << ", ";
        sum += k;
    }
    cout << endl;
    
    cout << sum << endl;
}

4. Имате масива {1,2,3,4,5,6,7,8,9,10} . Запишете във всяка клетка старата и стойност на квадрат. Използвайте референция.

Решение
#include <iostream>
using namespace std;

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    for (int &x : arr) {
        x *= x;
    }

    for (const int& x : arr) {
        cout << x << " ";
    }
}

5. Изведете таблицата за умножение.

Решение
#include <iostream>
using namespace std;

int main() {
    for (int i = 1; i <= 10; i++) {
        for (int j = 1; j <= 10; j++) {
            cout << i * j << "\t";
        }
        cout << endl;
    }
}

6. Даден е масив. Сортирайте го.

int arr[20] = {12, 45, 7, 89, 23, 56, 1, 78, 34, 90,
               11, 67, 5, 99, 42, 18, 73, 26, 60, 3};
Решение
#include <iostream>
using namespace std;

int main() {
    int arr[20] = {12, 45, 7, 89, 23, 56, 1, 78, 34, 90,
                   11, 67, 5, 99, 42, 18, 73, 26, 60, 3};

    int n = 20;

    // сортиране
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
            }
        }
    }

    // извеждане
    for (int x : arr) {
        cout << x << " ";
    }
}

7*. Решете горната задача, като използвате функцията sort ot <algorithms>.

Решение
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int arr[20] = {12, 45, 7, 89, 23, 56, 1, 78, 34, 90,
                   11, 67, 5, 99, 42, 18, 73, 26, 60, 3};

    sort(arr, arr + 20);

    for (const int& x : arr) {
        cout << x << " ";
    }
}

8. Намерете най – голямото и най – малкото число в горния масив.

Решение
#include <iostream>
#include <limits>
using namespace std;

int main() {
    int arr[20] = {12, 45, 7, 89, 23, 56, 1, 78, 34, 90,
                   11, 67, 5, 99, 42, 18, 73, 26, 60, 3};

    int minVal = numeric_limits<int>::max(); // най-голямото възможно int
    int maxVal = numeric_limits<int>::min(); // най-малкото възможно int

    for (int x : arr) {
        if (x < minVal) minVal = x;
        if (x > maxVal) maxVal = x;
    }

    cout << "Min = " << minVal << endl;
    cout << "Max = " << maxVal << endl;
}

9*. Решете задача 8 като използвате min_element и max_element от библиотеката с алгоритми на C++ (вижте връзката в задача 7).

Решение
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int arr[20] = {12, 45, 7, 89, 23, 56, 1, 78, 34, 90,
                   11, 67, 5, 99, 42, 18, 73, 26, 60, 3};

    int* minPtr = min_element(arr, arr + 20);
    int* maxPtr = max_element(arr, arr + 20);

    cout << "Min = " << *minPtr << endl;
    cout << "Max = " << *maxPtr << endl;
}