Подтвердить что ты не робот

Операции итератора с использованием LINQ-подобных операций на С++

Будучи запятнанным Линком, я не хочу отказываться от него. Однако для некоторых вещей мне просто нужно использовать С++.

Реальная сила linq как пользователя linq (то есть для меня) заключается не в деревьях выражений (которые сложно манипулировать), но и в легкости, с которой я могу смешивать и сопоставлять различные функции. Существуют ли эквиваленты .Where, .Select и .SelectMany, .Skip и .Take и .Concat для итераторов стиля С++?

Это было бы очень удобно для всех видов кода, который я пишу.

Мне не нужны LINQ-специфики, ключевая проблема здесь заключается в том, чтобы иметь возможность выражать алгоритмы на более высоком уровне, а не для кода на С++, как С# 3.0. Я хотел бы иметь возможность выразить: "результат формируется путем конкатенации первых n элементов каждой последовательности", а затем повторно использовать такое выражение везде, где требуется новая последовательность, - без необходимости вручную (и жадно) создавать промежуточные промежуточные продукты.

4b9b3361

Ответ 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.