Реализована библиотека из нескольких классов и функций, которая позволяет обрабатывать последовательные данные в одну несложную команду, похожую по синтаксису на LINQ (Fluent interface) и по духу — на потоки Java 8.
Пример 1:
int xs[] = { 1, 2, 3, 4, 5 };
std::vector<int> res =
from(xs, xs + 5) // Взять элементы xs
.select([](int x) { return x * x; }) // Возвести в квадрат
.where_neq(25) // Оставить только значения != 25
.where([](int x) { return x > 3; }) // Оставить только значения > 3
.drop(2) // Убрать два элемента из начала
.to_vector(); // Преобразовать результат в вектор
Пример 2:
std::istream_iterator<int> in(std::cin), eof;
std::cout <<
from(in, eof) // Взять числа из входного потока
.take(4) // Не более четырёх чисел
.until_eq(-1) // Перестать читать после прочтения -1
.to_vector(); // Получить список считанных чисел
Пример 3:
int xs[] = { 1, 2, 3, 4, 5 };
std::vector<double> res =
from(xs, xs + 5) // Взять элементы xs
.select<double>([](int x) { return sqrt(x); }) // Извлечь корень
.to_vector(); // Преобразовать результат в вектор
Пример 4:
std::istream_iterator<int> in(std::cin), eof;
std::ostream_iterator<double> out(std::cout, "\n");
from(in, eof) // Взять числа из входного потока
.select([](int x) { return sqrt(x); }) // Извлечь из каждого корень
.copy_to(out); // Вывести на экран
- Генерация последовательностей:
template<typename T> ??? from(T begin, T end)
— генерирует последовательность из STL-полуинтервала[begin; end)
. Исходный контейнер не должен изменяться никаким образом.
- Преобразования последовательностей:
.drop(count)
— удаляет из последовательности первыеcount
элементов. Если последовательность была короче, она становится пустой..take(count)
— оставляет в последовательности только первыеcount
элементов. В частности, из-за ленивости элементcount+1
никогда не должны быть запрошен у генератора последовательности. Если последовательность была короче, остаётся вся последовательность..select(f)
— применяет функциюf
к каждом элементу последовательности.f
может изменять тип элемента — в таком случае требуется явное указание нового типа синтаксисом.select<T>(f)
(как в примере 3). Вообще говоря, можно решить и без этого при помощиdecltype
иstd::declval<>
, но мы не будем..until(f)
— обрывает последовательность, как только встретит элемент, удовлетворяющий предикатуf
, или если последовательность закончилась. В частности, из-за ленивости все последующие элементы никогда не должны спрашиваться у предыдущих команд..until_eq(v)
обрывает последовательность, как только встретит элемент, равныйv
, или если последовательность закончилась..where(f)
— оставляет в последовательности только те элементы, которые удовлетворяют предикатуf
.f
— это произвольный функциональный объект (функция, функтор, лямбда)..where_neq(v)
— оставляет в последовательность только элементы, не равныеv
.
- Терминальные команды:
.to_vector()
— собирает все элементы в вектор и возвращает его..copy_to(iter)
— копирует сгенерированную последовательность в STL-подобный итераторiter
(аналогично алгоритмуstd::copy
). Например, это может бытьostream_iterator
(пример 4),back_inserter
или обычный итератор (если мы заранее знаем количество элементов).
- Все элементы, возникающие в процессе вычислений, имеют тип с:
- Конструктором по умолчанию.
- Конструктором копирования и оператором присваивания.
- Опционально: конструктором перемещения и оператором перемещающего присваивания. По возможности следует использоавть их.
- Все функторы поддерживают:
- Вызов константного
operator()
. - Перемещение.
- Вызов константного
-
Представлен файл
smoke_test.cpp
для неполной проверки корректности решения. Если расположить его в папкеlab_14/src
, то следующая команда, запущенная из папкиlab_14
, должна завершиться успешно:g++ -std=c++17 -pedantic -Wall -Wextra -Werror src/smoke_test.cpp -Iinclude -o smoke_test && ./smoke_test && echo OK