Операции итератора с использованием LINQ-подобных операций на С++
Будучи запятнанным Линком, я не хочу отказываться от него. Однако для некоторых вещей мне просто нужно использовать С++.
Реальная сила linq как пользователя linq (то есть для меня) заключается не в деревьях выражений (которые сложно манипулировать), но и в легкости, с которой я могу смешивать и сопоставлять различные функции. Существуют ли эквиваленты .Where
, .Select
и .SelectMany
, .Skip
и .Take
и .Concat
для итераторов стиля С++?
Это было бы очень удобно для всех видов кода, который я пишу.
Мне не нужны LINQ-специфики, ключевая проблема здесь заключается в том, чтобы иметь возможность выражать алгоритмы на более высоком уровне, а не для кода на С++, как С# 3.0. Я хотел бы иметь возможность выразить: "результат формируется путем конкатенации первых n элементов каждой последовательности", а затем повторно использовать такое выражение везде, где требуется новая последовательность, - без необходимости вручную (и жадно) создавать промежуточные промежуточные продукты.
Ответы
Ответ 1
У меня нет конкретного опыта работы с LINQ, но библиотека make_filter_iterator:
std::vector<int> vec = ...;
// An iterator skipping values less than "2":
boost::make_filter_iterator(_1 > 2, vec.begin())
LINQ "Выберите" карты в make_transform_iterator
:
using namespace boost::lambda;
//An iterator over strings of length corresponding to the value
//of each element in "vec"
//For example, 2 yields "**", 3 "***" and so on.
boost::make_transform_iterator(construct<std::string>('*', _1), vec.begin())
И они могут быть составлены:
//An iterator over strings of length corresponding to the value of each element
// in "vec", excluding those less than 2
std::vector<int> vec = ...;
boost::make_transform_iterator(construct<std::string>('*', _1),
boost::make_filter_iterator(_1 > 2, vec.begin())
)
Однако, есть несколько неприятных вещей с этим:
- Тип, возвращаемый
make_xxx_iterator(some_functor, some_other_iterator)
, это xxx_iterator<type_of_some_functor, type_of_some_iterator>
- Тип функтора, созданного с помощью boost:: bind, лямбда или феникса, быстро становится неуправляемо большим и громоздким для записи.
Вот почему я избегал в приведенном выше коде, чтобы назначить результат make_xxx_iterator
переменной. Функция С++ 0x "auto" будет очень приветствуемой.
Но тем не менее, итератор С++ не может жить "один": они должны попадать в пары, чтобы быть полезными. Итак, даже с "авто", это все еще глоток:
auto begin = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.begin())
);
auto end = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.end())
);
Избегание использования лямбда делает вещи многословными, но управляемыми:
struct MakeStringOf{
MakeStringOf(char C) : m_C(C){}
char m_C;
std::string operator()(int i){return std::string(m_C, i);}
};
struct IsGreaterThan{
IsGreaterThan(int I) : m_I(I){}
int m_I;
bool operator()(int i){return i > m_I;}
};
typedef boost::filter_iterator<
IsGreaterThan,
std::vector<int>::iterator
> filtered;
typedef boost::transform_iterator<
MakeStringOf,
filtered
> filtered_and_transformed;
filtered_and_transformed begin(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.begin())
);
filtered_and_transformed end(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.end())
);
В этом отношении многообещающая библиотека (Boost.RangeEx) (еще не) Boeing. Она позволяет комбинировать два итератора в одном диапазоне. Что-то вроде:
auto filtered_and_transformed = make_transform_range(
make_filter_range(vec, _1 > 2),
construct<std::string>('*', _1)
);
Ответ 2
Я работаю над (С# LINQ) -подобной библиотекой только для заголовков С++.
Вот он: http://code.google.com/p/boolinq/
Я хотел бы получить любую обратную связь...
UPDATE:
Вот новая ссылка на boolinq 2.0: https://github.com/k06a/boolinq
Все исходные коды основаны в одном файле заголовка - https://github.com/k06a/boolinq/blob/master/boolinq/boolinq.h
Это супер короткое: менее 800 строк для примерно 60 различных операций!
Ответ 3
Я бы рекомендовал библиотеку P-Stade.Oven для вашей справки. Это сильно усиленная библиотека, работающая на диапазонах STL и имеющая множество LINQ-подобных функций, включая эквиваленты .Where,.Select.Skip.Take и .Concat.
Ответ 4
Смотрите этот поток Google Groups.
vector<int> numbers = {1, 2, 3, 4, 8, 5, 9 , 24, 19, 15, 12 }
auto query =
from(numbers).
where([](int i) { return i < 15 && i > 10}).
select(fields::full_object);
Я не мог найти ничего более или менее "официального" или широко принятого, но вы можете попытаться связаться с автором оригинального сообщения.
Ответ 5
С Boost.Range и Linq в С++ 11, вы можете написать запросы Linq очень похожим образом:
std::vector<int> numbers = { 1, 2, 3, 4 };
auto r = LINQ(from(x, numbers) where(x > 2) select(x * x));
for (auto x : r) printf("%i\n", x);
Будет выводиться:
9
16
Ответ 6
Вот еще одна альтернатива которая представляет собой просто оболочку вокруг алгоритмов boost и stl и, таким образом, вы получаете все преимущества производительности этих реализаций.
Он работает следующим образом:
std::vector<int> xs;
auto count = from(xs)
.select([](int x){return x*x;})
.where([](int x){return x > 16;})
.count();
auto xs2 = from(xs)
.select([](int x){return x*x;})
.to<std::vector<int>>();
Обратите внимание, что некоторые методы возвращают прокси для пустых диапазонов, например.
std::vector<int> xs;
auto max = from(xs)
.select([](int x){return x*x;})
.where([](int x){return x > 16;})
.max()
.value_or(0);
Ответ 7
Лично я использовал cpplinq время от времени. Это очень хорошо. Он не пытается быть идеальным переводом LINQ любыми средствами и поддерживает достаточную идентификацию C++, если хотите, что делает ее довольно сильной и само по себе. Кроме того, вы не принимаете никаких зависимостей, кроме C++ 11 стандартов, то есть. Пока вы можете это переварить, вам хорошо пойти с cpplinq.
Ответ 8
Библиотека Abseil имеет множество https://github.com/abseil/abseil-cpp/blob/master/absl/algorithm/container.h и много функций контейнера: c_all_of
, c_any_of
, c_none_of
, c_find
, c_count
, c_count_if
, c_replace_copy
, c_unique_copy
, c_min_element
, c_max_element
, c_accumulate