0:00
Рассмотрим следующую задачу.
Допустим, мы хотим посчитать,
сколько конкретного значения элемента содержится в нашем контейнере.
Например, сколько раз двойка встречается в векторе.
Для этого мы, конечно же, можем воспользоваться циклом range-based for,
что мы и сделаем.
for auto айтем в нашем векторе.
И при этом нам надо завести ещё и счётчик,
в котором будет количество нужных нам встречаний.
Удалим печати.
Значит, тренируемся по каждому айтему.
Если i равно двум,
то инкрементируем наш счётчик.
После этого выводим значение счётчика.
Значит, запускаем наш код.
Мы видим, что он работает, то есть вывелась единичка,
потому что двойка встречается один раз.
Давайте попробуем какой-нибудь другой элемент.
Например, минус один.
Запустим наш код, видим ноль.
Вопрос, что мы сделали неправильно в этом коде, хотя он и работает?
Конечно же, мы написали цикл range-based for,
ведь наверняка это уже есть в стандартной библиотеке.
Наверняка функция подсчёта элементов уже хранится, и да, конечно же, мы правы,
есть функция count.
count так же, как и функция сортировки, принимает диапазон,
то есть начало и конец интервалов, на которых ей надо отработать.
Давайте же скорее вызовем её.
Мы можем её значения, её результат, сразу передавать потоку cout,
то есть сразу делать вывод, вызываем функцию count.
begin от нашего вектора, end от нашего вектора, и третьим аргументом
она принимает элемент, количество вхождений которого надо посчитать.
Удалим лишний вывод.
И видим единичку, то есть двойка встречается один раз.
Давайте, ради интереса добавим ещё одну двойку.
Видим, что число стало два, то есть двойка встречается два раза.
Алгоритм работает, это замечательно.
Смотрите, всё получилось в одну строчку, без заведения дополнительных переменных,
без range-based for и, главное, без багов, потому что этот код,
он уже отлажен и проверен.
Что ж, на самом деле, намного чаще возникает задача посчитать
количество элементов, которое обладает некоторым свойством.
Например, количество чётных элементов или количество элементов больше какого-то
другого элемента, например, больше двух.
Давайте решим такую задачу.
То есть нам надо посчитать количество элементов в нашем контейнере,
которые больше двоечки.
Я не буду писать цикл range-based for, потому что вы уже знаете,
что наверняка эта функция написана в стандартной библиотеке.
И для этого действительно есть функция, называется она count if.
Третьим аргументом ей надо передать функцию,
которая на вход будет принимать элемент, а возвращать true либо false,
выполнилось или нет для этого элемента нужное нам условия.
В данном случае нужное нам условие — это больше ли элемент, чем два.
Давайте определим внешнюю функцию.
void, точнее, нет.
Я уже сказал, что она возвращает bool.
Назовём её Gt2, greater два.
На вход она будет принимать int x, какой-то x.
Ну и, соответственно, если x больше двух, то мы возвращаем true.
Иначе мы возвращаем false.
Отлично.
Теперь вот эту функцию мы можем прямо передать в алгоритм count if.
Запустим наш код.
Видим, что элементов больше, чем два, ровно три.
А именно это один, три...
Точнее, это три, пять и четыре.
Всё остальные меньше двух.
По аналогии мы можем определить функцию меньше двух.
Давайте ещё удалим лишнюю двоечку, чтобы она нас не смущала.
Назовём её Lt2.
И поменяем в ней просто условие.
Если х меньше двух, то вернём true, иначе false.
Запустим нашу функцию.
Добавим перенос строки.
И видим, что элементов больше двух,
их три, а меньше двух, их один, единичка.
Отлично, всё работает, но в этом коде в принципе есть небольшой недостаток.
Заключается он как минимум вот в чём.
Смотрите, мы здесь делаем вызов функции Gt2, здесь мы делаем вызов функции Lt2.
Они, вообще, довольно специализированные, то есть странно предположить,
что кому-то в стандартной библиотеке понадобилась функция, которая определяет,
больше ли входной элемент, чем два.
Это наша функция, которую мы определили для своих нужд.
При этом, данная функция определена достаточно далеко от входа,
в котором мы её используем.
Смотрите, вот мы её вызываем, Gt2, но мы не видим её определение при этом.
Вот нам надо туда переходить.
После этого мы видим внутренний код.
И, на самом деле, велика вероятность,
что эта функция будет использоваться всего один раз в вашей программе.
Всего один раз в каком-то месте вам надо узнать, больше ли ваши элементы,
чем два, количество таких элементов.
Значит, что в этом месте предлагает язык C++?
Лямбда-выражение.
Лямбда-выражение — это способ написать функцию на лету, прямо мо месту вызова.
Как это выглядит?
Давайте рассмотрим просто, как же здесь написать функцию на лету,
чтобы не определять внешнюю функцию Gt2.
Начинается всё с квадратных скобок.
Они говорят о том, что дальше идёт лямбда-выражение.
После этого мы пишем сигнатуру функции прямо так, как она есть.
То есть мы опускаем, пока возвращаем Lt.
Но дальше идут круглые скобки, в которых мы пишем аргументы,
которые должна принимать эта функция, после чего мы напишем тело данной функции.
Сказано — сделано.
Аргумент у нас — целое число.
Ну а тело функции мы можем, на самом деле,
скопировать прямо вот отсюда.
И удалим функцию Gt2.
То есть смотрите.
Что происходит в этом коде?
Мы запускаем алгоритм count if, передаём ему начало интервала,
передаём ему конец интервала.
А затем прямо передаём функцию, которую он должен вызывать,
для того чтобы осуществить свою работу.
То есть лямбда-выражение.
Здесь мы говорим, что это лямбда-выражение принимает на вход целое число,
а дальше в его теле мы пишем, что если переданное число больше двух,
то возвращаем true, иначе false.
Давайте удалим ещё вот этот вызов.
Нам он больше не понадобится.
И видим, что, действительно, элементов больше двух, их три.
На самом деле, давайте попробуем ещё обобщить эту задачу.
Сейчас мы двойку явно закодили в код.
Давайте предположим, что число, с которым мы хотим делать сравнение,
оно будет приходить откуда-то из консоли.
То есть давайте объявим переменную thr, threshold, порог.
По умолчанию, присвою в него ноль.
А дальше считаем из потока cout наш порог.
После этого, попробуем использовать threshold внутри нашей функции.
Собираем наш код и видим множество ошибок компиляции,
одни из них гласит: thr, threshold, is not captured.
Что это означает?
Что threshold не захвачен.
Кто его захватил?
Ужас! Никто не захватил и мы не можем его
использовать.
Очень непонятное сообщение, давайте разберём его по порядку.
Представим, что у нас объявлена действительно внешняя функция, вот Lt2.
Согласитесь с тем, что если мы в ней вот здесь вот начнём использовать threshold,
то будет тоже ошибка, потому что функция Lt2 не знает, что это за threshold,
откуда надо взять эту переменную.
То же самое происходит и с лямбда-выражениями,
эта функция и она не знает, откуда взять этот порог, что это за threshold,
как его использовать и кто его даст.
Для того, чтобы лямбда-выражению сказать, откуда можно взять переменную, которая
используется внутри и приходит из внешнего контекста, используются квадратные скобки.
То есть здесь мы можем написать переменную thr, threshold.
Этим мы скажем, что возьми из контекста, в которым ты сейчас создаёшься,
вот эту вот переменную и сделай её доступной внутри себя.
Внутри себя — это в этих строчках.
Ну и теперь мы можем смело начинать её использовать.
if x больше, чем порог...
Так, здесь, конечно же, мы считываем из cin'а.
Спускаем наш код.
Он скомпилировался.
То есть смотрите, теперь переменная threshold стала доступна во
внутреннем контексте нашей функции.
Запускаем наш код.
Он ждёт от меня вывод, ввод.
Давайте введём десять, на выводе, на выходе мы получили ноль.
У нас ноль элементов больше десяти.
Запустим наш код ещё раз, введём, например, ноль,
и видим, что у нас пять элементов больше нуля.
Ну и финальный тест.
Введём три, получим два.
У нас больше трёх всего два элемента, это четыре и пять.
В данном видео мы познакомились с алгоритмами, которые позволяют сказать,
сколько конкретного элемента находится в той или иной коллекции,
а также найти количество элементов, для которых выполняется определённое условие,
например, чётные или нечётные.
Алгоритмы, которые позволяют это сделать, соответственно, count и count if.
Также мы познакомились с лямбда-выражениями,
лямбда-выражения позволяют написать функцию на лету.
Очень рекомендуется их использовать в тех случаях,
когда вы используете довольно специализированную версию функции,
которой нету в стандартной библиотеке и которая при этом довольно короткая.
Другими словами, одна строчка.
На слайде у меня приведено лямбда-выражение.
Напомню, лямбда-выражения начинаются с квадратных скобок, в которых мы указываем
переменные, которые мы хотим передать в контекст данного лямбда-выражения.
Напомню, в нашей задаче это был threshold, то есть порог,
с которым мы сравниваем элемент.
Дальше мы указываем аргументы, которые будет принимать функция, которая создаётся
данным лямбда-выражением, то есть передаём туда целое число.
Дальше в фигурных скобках мы пишем тело лямбда-выражения,
то есть проверку условия, что наше число больше порога.
В следующем видео мы узнаем простой способ модифицировать элементы контейнера.