Библиотека представляет из себя реализации адаптеров для упрощенной работы с алгоритмами, контейнерами и файлами. Обобщенный подход к алгоримам и итераторам позволяет более элегантно решать типовые задачи.
Например, для решения задачи нахождения частотности слов среди всех текстовых файлов директории, код может выглядеть следующим образом:
Dir(argv[1], recursive)
| Filter([](std::filesystem::path& p){ return p.extension() == ".txt"; })
| OpenFiles()
| Split("\n ,.;")
| Transform(
[](std::string& token) {
std::transform(token.begin(), token.end(), token.begin(), [](char c){return std::tolower(c);});
return token;
})
| AggregateByKey(
0uz,
[](const std::string&, size_t& count) { ++count;},
[](const std::string& token) { return token;}
)
| Transform([](const std::pair<std::string, size_t>& stat) { return std::format("{} - {}", stat.first, stat.second);})
| Out(std::cout);Еще одним значимым отличием такого подхода от классических является то, что вычисления являются могут быть ленивыми, а создаваемые объекты не владеют массивом данных для решения данной задачи. Подобный подход, в частности, применяется в классах std::string_view и std::span
- Dir - берет все файлы в директории (и рекурсивно по всем поддиректориям)
- OpenFiles - открывает файловый поток для каждого пути из предыдущего адаптера
- Split - делит входной поток по списку делимитеров передаваемых через аргументы
- Out - выводит данные в выходной поток
- AsDataFlow - преобразует контейнер в поток данных для дальнейшей обработки
- Transform - изменяет значения элементов, наподобие того как это делает алгоритм transform, применяя заданную функцию к каждому элементу
- Filter - фильтрация по определенному признаку, признак передается в качестве аргумента
- Write - проходится по всем элементам входного диапазона и записать их в указанный поток вывода, вставляя между элементами (а также после каждого элемента) заданный разделитель.
- AsVector - собирает результаты обработки в вектор
- Join - объединяет два потока данных по ключу, аналогично операции LEFT JOIN в SQL
- KV - структура ключ-значение, используемая для операций объединения
- JoinResult - результат операции объединения, содержащий данные из обоих потоков
- DropNullopt - фильтрует
std::optinal<T>поток отstd::nulloptзначений - SplitExpected - в случае если предыдущий адаптер возвращает expeceted, позволяет разделить пайплайн обработки на 2 для ожидаемых и нет результатов
- Take — берёт первые n элементов.
- Skip — пропускает первые n элементов.
- NonEmptySplit — делит строки по разделителям и удаляет пустые токены.
- Enumerate — нумерует элементы:
T → (index, T), индекс с 0. - AsSet — удаляет дубликаты, оставляя первое вхождение (distinct).
- CountIf(pred) — возвращает число элементов, удовлетворяющих
pred. Терминальная, не ленивая. - DropExpected — из
expected<T, E>оставляет ошибки: потокE. - DropErrors — из
expected<T, E>оставляет успешные значения: потокT. - Chunk(size) — группирует элементы в блоки по
size: потокstd::vector<T>(последний блок может быть короче). - Zip(flow) — объединяет два потока по позициям (аналог
zip_longest); результат —Zipped<L, R>сstd::optionalполями. - Zipped<L, R> — контейнер результата
Zip: параstd::optional<L>,std::optional<R>; поддерживает сравнение на равенство. - AggregateByKey - агрегация значений относительно соответствующего ключа. Значение, соответствующее ключу, обновляется через переданный функциональный объект - агрегатор. Выполняется не лениво
- Пример:
aggregator := [int value{}](char c) { value++; return value; } [ a, b, c, d, a, a, b, d ] -> [ (a, 3), (b, 2), (c,1), (d,2) ]
- Пример:
Все вышеуказанные сущности покрыты тестами, с помощью фреймворка Google Test.