2009-10-14 2 views
12

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

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

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

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

+0

Возможный дубликат: http://stackoverflow.com/questions/232222/is-there-a-linq-library-for-c --then снова они могут отличаться, я сомневался, что это действительно " точный 'duplicate –

+2

Я не интересуюсь LINQ специально, но в обработке списка функционального стиля на C++. –

+0

В частности, лямбда хороша, но не имеет решающего значения. –

ответ

4

У меня нет конкретного опыта работы с LINQ, но библиотека Boost.Iterator, похоже, подходит к тому, о чем вы говорите.

Идея состоит в том, чтобы иметь функции (IIUC, в LINQ, они принимают форму методов расширения, но это не принципиально), принимая итератор и функцию, объединяя их для создания нового итератора.

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>
  • Тип функтора создан с использованием подталкивание :: привязывать, лямбда, или феникс быстро становится неуправляемо большим и громоздким писать.

Именно поэтому я избегал в приведенном выше коде, чтобы присвоить результат make_xxx_iterator переменной. Функция C++ 0x «auto» будет очень приветствуемой.

Но все же Итератор C++ не может жить «в одиночку»: они должны попадать в пары, чтобы быть полезными.Таким образом, даже с "авто", это все еще глоток:

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()) 
); 

The (не-пока) библиотека Boost.RangeEx является многообещающий в этом отношении, поскольку он позволяет объединить два итератора в одном диапазоне. Что-то вроде:

auto filtered_and_transformed = make_transform_range(
    make_filter_range(vec, _1 > 2), 
    construct<std::string>('*', _1) 
); 
4

См. this Тема Группы Google.

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); 

Я не мог найти что-нибудь более или менее «официальное» или общепринятое, но вы можете попробовать связаться с автором оригинального поста.

+0

То, что использует компилятор C++ 0x с поддержкой auto и лямбда, может стать немного более громоздким без lambdas ('[] (int i) {...}') –

+2

Спасибо, это аккуратно! Тем не менее, связанный поток не легко доступен, и в любом случае он выглядит слишком сфокусированным на LINQ - мне все равно, о синтаксисе, я просто хочу map/reduce/filter/concat и т. Д. старый функциональный стиль, такой как STL, накапливается достаточно - до тех пор, пока он используется * сегодня * ... –

6

Я хотел бы рекомендовать P-Stade.Oven библиотеку для справки. Это сильно усиленная библиотека, работающая на диапазонах STL и имеющая множество LINQ-подобных функций, включая эквиваленты .Where, .Select .Skip .Take и .Concat.

6

Я работаю над (C# LINQ) -подобной библиотекой только для заголовков C++.

Здесь: 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 разные операции!

+0

библиотека выглядит очень красиво, и ее ближайший я видел что-то работающее, как ожидалось. Однако это не для меня для gcc 4.7 с включенным C++ 11 – lurscher

+0

@lurscher, эта библиотека была разработана с использованием Visual C++.Но я бы хотел сделать его настолько портативным и кросс-платформенным, насколько это возможно. Я попытался поддержать mingw 4.4, но он не поддерживает C++ 11 lambdas. Я скоро добавлю поддержку gcc. Или вы можете мне помочь) – k06a

3

С Boost.Range и Linq в C++ 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); 

Will:

9 
16 
+0

'from' и' where' являются макросами? – CodesInChaos

+0

нет, это не так. Единственный макрос - это макрос 'LINQ'. Остальное анализируется с использованием препроцессора. –

2

Вот еще alternative, что это просто обертки вокруг алгоритмов 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); 
+0

Хорошо, но несколько вещей: 'void for_each (...) {return ...; } 'не звучит правильно, и всякий раз, когда у вас есть' auto x (...) -> decltype (некоторое длинное выражение) 'и вам нужно повторно использовать этот тип внутри' x' (например, 'select',' reverse ',' take' и т. д.), просто скажите 'decltype (x (...))' и передайте все аргументы, для многих вещей будет намного * короче. Кроме того, у вас есть 'to_vector', а не' '. :) – Xeo

+0

Xeo: Спасибо за отзыв. Исправлено 'to' и' for_each'. Хотя я не совсем понимаю, как получить ваше предложение об сокращении 'decltype'''''''а работы. Не могли бы вы привести мне более конкретный пример? – ronag

+0

Оглядываясь назад, я удивляюсь, почему вам нужно повторно использовать тип вообще в теле, так как конструктор 'linq_range' не является« явным ». – Xeo

Смежные вопросы