Skip to content

C++ equivalent of Python built-in function zip.

License

Notifications You must be signed in to change notification settings

D-Sokol/zip-cpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zip-cpp

Zip-cpp - это написанная на C++ библиотека, представляющая шаблонную функцию zip, имитирующую поведение одноименной функции в Python 3. Эта функция возвращает объект, поддерживающий семантику range-based for и позволяющий параллельно итерироваться по нескольким контейнерам.

Возможности библиотеки

Функция zip поддерживает следующие контейнеры:

  • Все контейнеры STL (vector, string, set, map, list, ...).
  • Массивы в стиле C (int[10]), при условии, что тип соответствующего выражения не был преобразован в указатель.
  • Другие объекты, полученные ранее при помощи функции zip.
  • Любые диапазоны, заданные двумя итераторами, удовлетворяющими требованиям категории InputIterator и шаблона iterator_traits, в том числе, определенными пользователем. Пару итераторов необходимо скомпоновать в единый объект, при помощи тривиального класса-обертки zipcpp::IterRange или его аналога. Пример использования этой возможности для итераторов потока ввода и числовой последовательности приведен в файле advanced_examples.cpp.

Категория итератора контейнера, возвращаемого функцией zip, соответствует наиболее общей категории итераторов переданных контейнеров. Например, если в функцию были переданы только вектора, то возвращается итератор, соответствующий категории RandomAccessIterator. Если функция zip была вызвана без аргументов, то возвращается пустой диапазон, заданный итераторами категории InputIterator.

Поддерживаются все операции, требуемые для итераторов соответствующей категории со следующими исключениями:

  1. Не поддерживается оператор -> для непосредственного доступа к методам кортежа значений, поскольку разыменование итератора возвращает временный объект.
  2. Поскольку значение, возвращаемое разыменованным итератором, содержит кортеж ссылок, обмен значений при помощи временной переменной, некорректен (фактически, эквивалентен присваиванию *it1 = *it2)
// Некорректно, так как element хранит ссылки на те же объекты, что и *it1
auto element = move(*it1);
*it1 = std::move(*it2);
*it2 = std::move(element);
// Корректно
using std::swap;
swap(*it1, *it2);
// Корректно
std::iter_swap(it1, it2);

Интерфейс библиотеки

Открытым интерфейсом библиотеки являются следующие функции и классы, вложенные в пространство имен zipcpp:

  • zip - шаблонная функция, принимающая любое количество контейнеров и возвращающая объект, который можно рассматривать как "контейнер кортежей ссылок".
  • IterRange - шаблонный класс-контейнер, принимающий пару итераторов одного типа и представляющий заданный ими диапазон.

Пример использования

Использование zip в python

Вызов zip используется для прохождения по произвольному количеству итерируемых объектов произвольных типов. Например, следующий код:

iterable1 = [1, 2, 3, 4]
iterable2 = "abcde"  # Этот контейнер длиннее остальных
iterable3 = {40, 30, 20, 10}
for v1, v2, v3 in zip(iterable1, iterable2, iterable3):
    print(v1, v2, v3)

генерирует приведенный ниже вывод с точностью до порядка элементов в множестве:

1 a 40
2 b 10
3 c 20
4 d 30

При этом цикл завершается, как только итерация достигает конца хотя бы одного из диапазонов.

Использование zip в C++

При помощи данной библиотеки приведенный выше код на Python можно переписать на C++ с сохранением семантики:

#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
#include "zip.h"

using zipcpp::zip;

int main(int, char**) {
    std::vector<int> iterable1 = {1, 2, 3, 4};
    std::string iterable2 = "abcde";
    std::unordered_set<int> iterable3 = {40, 30, 20, 10};
    for (const auto& [v1, v2, v3] : zip(iterable1, iterable2, iterable3)) {
        std::cout << v1 << ' '
                  << v2 << ' '
                  << v3 << std::endl;
    }
    return 0;
}

Ограничения

Итераторы, взятые от переданных контейнеров, не должны инвалидироваться в течение всего срока жизни объекта Zip. В частности, вызов zip(zip(a, b), zip(c, d)) некорректен: следует сохранить оба аргумента в отдельные переменные.

Фактически тип, возвращаемый выражением *zip_iterator - обертка над кортежем ссылок, таким, как, std::tuple<int&, const int&>. Константность ссылки соответствует константности переданного контейнера. Исключением является случай вложенных вызовов zip, в котором вложенные кортежи возвращаются по значению, а не по ссылке.

Кортеж можно использовать для инициализации отдельных переменных при помощи structured binding declaration:

const auto& [var1, var2, var3] = *zip_iterator;

В таком объявлении необходимо использовать либо const auto&, чтобы переменные имели ссылочный тип (при этом ссылки не обязательно будут константными!), либо auto для копирования значений.

Использование библиотеки

Все классы и функции, необходимые для использования zip, находятся в заголовочном файле zip.h. Остальные файлы с исходным кодом в данном репозитории предоставляют юнит-тесты для библиотеки, а также функцию main, вызывающую эти тесты.

Наиболее простым способом использования данной библиотеки в другом проекте является копирование файла zip.h.

В качестве альтернативного решения для проектов, использующих Git и CMake можно добавить данный репозиторий в качестве подмодуля и соответствующим образом настроить команды add_subdirectory, target_include_directories, target_link_libraries.

Для сборки проекта, использующего данную библиотеку, необходимо использовать версию стандарта не ниже C++17.

About

C++ equivalent of Python built-in function zip.

Resources

License

Stars

Watchers

Forks

Packages

No packages published