Отдельный класс задач, который не может обойтись без циклов, называется агрегированием данных. К таким задачам относятся поиск максимального, минимального, суммы, среднего арифметического и т.п. Их главная особенность в том, что результат зависит от всего набора данных. Для рассчета суммы нужно сложить все числа, для вычисления максимального нужно сравнить все числа.
С такими задачами хорошо знакомы все, кто занимаются числами, например бухгалтеры или маркетологи. Обычно их выполняют в таблицах наподобие Microsoft Excel или Google Sheets.
Разберём самый простой пример – поиск суммы набора чисел. Реализуем функцию, которая складывает числа в указанном диапазоне, включая границы. Диапазоном в данном случае называется ряд чисел от какого-то начала до определенного конца. Например, диапазон [1, 10] включает в себя все целые числа от 1 до 10.
<?php
sumNumbersFromRange(5, 7); // 5 + 6 + 7 = 18
sumNumbersFromRange(1, 2); // 1 + 2 = 3
// [1, 1] диапазон с одинаковым началом и концом – тоже диапазон
// он в себя включает ровно одно число – саму границу диапазона
sumNumbersFromRange(1, 1); // 1
sumNumbersFromRange(100, 100); // 100
Для реализации этого кода нам понадобится цикл, так как сложение чисел – это итеративный процесс (он повторяется для каждого числа), а количество итераций зависит от размера диапазона. Перед тем, как смотреть код, попробуйте ответьте на вопросы ниже:
Попробуйте сначала подумать над этими вопросами, а затем посмотрите код ниже:
<?php
function sumNumbersFromRange ($start, $finish)
{
// Технически можно менять $start
// Но входные аргументы нужно оставлять в исходном значении
// Это сделает код проще для анализа
$i = $start;
$sum = 0; // Инициализация суммы
while ($i <= $finish) { // Двигаемся до конца диапазона
$sum = $sum + $i; // Считаем сумму для каждого числа
$i = $i + 1; // Переходим к следующему числу в диапазоне
}
// Возвращаем получившийся результат
return $sum;
};
Общая структура цикла здесь стандартна. Есть счетчик, который инициализируется начальным значением диапазона, есть сам цикл с условием остановки при достижении конца диапазона, и, наконец, изменение счетчика в конце тела цикла. Количество итераций в таком цикле равно $finish - $start + 1
. То есть для диапазона от 5 до 7 – это 7 - 5 + 1, то есть 3 итерации.
Главные отличия от обычной обработки связаны с логикой вычислений результата. В задачах на агрегацию всегда есть какая-то переменная, которая хранит внутри себя результат работы цикла. В коде выше это $sum
. На каждой итерации цикла происходит её изменение, прибавление следующего числа в диапазоне: $sum = $sum + $i
. Весь процесс выглядит так:
<?php
// Для вызова sumNumbersFromRange(2, 5);
$sum = 0;
$sum = $sum + 2; // 2
$sum = $sum + 3; // 5
$sum = $sum + 4; // 9
$sum = $sum + 5; // 14
// 14 – результат сложения чисел в диапазоне [2, 5]
У переменной $sum
есть начальное значение, равное 0. Зачем вообще задавать значение? Любая повторяющаяся операция начинается с какого-то значения. Нельзя просто так объявить переменную и начать с ней работать внутри цикла. Это может приводить к ошибкам:
<?php
// начальное значение не задано
// PHP автоматически делает его равным NULL
$sum;
// первая итерация цикла
$sum = $sum + 2; // ?
В результате такого вызова внутри $sum
окажется верный результат, но интерпретатор выведет ошибку: PHP Notice: Undefined variable: sum
. Она возникает из-за попытки использовать неопределённую переменную. Значит какое-то значение всё же нужно. Почему в коде выше выбран 0? Очень легко проверить, что все остальные варианты приведут к неверному результату. Если начальное значение будет равно 1, то результат получится на 1 больше, чем нужно.
В математике существует понятие нейтральный элемент операции (у каждой операции свой элемент). Это понятие имеет очень простой смысл. Операция с этим элементом не изменяет то значение, над которым проводится операция. В сложении любое число плюс ноль дает само число. При вычитании – тоже самое. Даже у конкатенации есть нейтральный элемент – это пустая строка: '' + 'one'
будет 'one'.
Вопрос на самопроверку. Какой нейтральный элемент у операции умножения?
Агрегация применяется не только к числам, но и к строкам. Это такие задачи, в которых строка формируется динамически, то есть заранее неизвестно, какого она размера и что будет содержать. Представьте себе функцию, которая умеет «умножать» строку, то есть она повторяет её указанное количество раз:
<?php
repeat('hexlet', 3); // 'hexlethexlethexlet'
Принцип работы этой функции довольно простой: в цикле происходит «наращивание» строки указанное количество раз:
<?php
function repeat($text, $times)
{
// Нейтральный элемент для строк – пустая строка
$result = '';
$i = 1;
while ($i <= $times) {
// Каждый раз добавляем строку к результату
$result = "{$result}{$text}";
$i = $i + 1;
}
return $result;
}
Распишем выполнение этого кода по шагам:
<?php
// Для вызова repeat('hexlet', 3);
$result = '';
$result = "{$result}hexlet"; // hexlet
$result = "{$result}hexlet"; // hexlethexlet
$result = "{$result}hexlet"; // hexlethexlethexlet
Циклы подходят не только для обработки чисел, но и при работе со строками. В первую очередь благодаря возможности получить конкретный символ по его индексу. Ниже пример кода, который распечатывает буквы каждого слова на отдельной строке:
<?php
function printNameBySymbol($name)
{
$i = 0;
// Такая проверка будет выполняться до конца строки
// включая последний символ. Его индекс `длина строки - 1`.
while ($i < strlen($name)) {
// Обращаемся к символу по индексу
print_r("$name[$i]\n");
$i = $i + 1;
}
}
$name = 'Arya';
printNameBySymbol($name);
// => 'A'
// => 'r'
// => 'y'
// => 'a'
Самое главное в этом коде, поставить правильное условие в while
. Это можно сделать сразу двумя способами: $i < strlen($name)
или $i <= strlen($name) - 1
. Оба способа приводят к одному результату.
Ещё одно использование циклов – формирование строк. Подобная задача нередко встречается в веб-программировании. Она сводится к обычной агрегации с применением интерполяции или конкатенации.
Есть одна задача, крайне популярная среди людей, проводящих собеседования, это переворот строки. Её можно решить множеством разных способов, но именно посимвольный перебор считается самым базовым. Пример работы этой функции:
<?php
reverse('Hexlet'); // telxeH
Общая идея переворота состоит в следующем: нужно брать символы по очереди с начала строки и соединять их в обратном порядке. Звучит довольно просто. Давайте проверим:
<?php
function reverse($str)
{
$i = 0;
// Нейтральный элемент для строк это пустая строка
$result = '';
while ($i < strlen($str)) {
$currentChar = $str[$i];
// Соединяем в обратном порядке
$result = "{$currentChar}{$result}";
// Тоже самое через конкатенацию
// $result = $currentChar . $result;
$i = $i + 1;
}
return $result;
}
$name = 'Bran';
reverse($name); // 'narB'
// Проверка нейтрального элемента
reverse(''); // ''
Единственный возможно сложный момент в этом коде – прочувствовать, как собирается сама строка. Так как каждый следующий символ прикрепляется к результирующей строке слева, то, в конечном итоге, строка оказывается перевернута.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт