- PVSM.RU - https://www.pvsm.ru -
Всё началось с того, что я в институте и после его окончания писал код на C++ и не знал бед. Но тут в один прекрасный день пришлось писать код под .NET на C#. Сперва немного поплевался, но потом ничего — втянулся. Увидел выгодные отличия от C++: безопасность, строгость и т.д. Также не смог обойти стороной LINQ при работе с коллекциями…
Но всю прелесть LINQ я оценил, когда настала пора вернуться к C++. Было немного непривычно писать на C++, после полугодового перерыва. Ничто не предвещало беды, как вдруг мне нужно было подсчитать сумму элементов в векторе, конкретнее сумму полей элементов вектора. На C# это решалось бы так:
int sum = candles.Sum(c => c.ClosePrice);
Но на C++ выходило:
int sum = 0; for(int i = 0; i < candles.size(); i++) sum += candles[i].ClosePrice;
А если переписать на итераторах:
int sum = 0; for(auto it = candles.begin(); it != candles.end(); ++it) sum += it->ClosePrice;
Qt чуть-чуть облегчает ситуацию, но не слишком:
int sum = 0; foreach(Candle candle, candles) sum += candle.ClosePrice;
Также новый стандарт языка C++11 обещает нам упрощение,
но Visual Studio 2010 увы эту фичу не поддерживает:
int sum = 0; for (Candle candle : vector) sum += candle.ClosePrice;
К хорошему быстро привыкаешь. Это был полнейший непорядок. Все эти варианты мне не подходили. Нужно было решение-однострочник. Дальше я стал гуглить и по первой же ссылке нашёл: http://stackoverflow.com/questions/3221812/sum-of-elements-in-a-stdvector [1]
Самое короткое из предложенных решений:
int sum = std::accumulate(vector.begin(), vector.end(), 0);
Но, что делать если складывать надо значения только одного из полей. Можно конечно сделать хитрый итератор, который при разыменовании — возвращает одно из полей… Но всё это попахивает жёстким кодингом для такой простой задачи.
Следующие 20-30 минут гугления показали что есть Boost Ranges [2] и парочка других библиотек, но они все выглядели не так как выглядит LINQ. В тот самый момент я почувствовал в себе силы — написать свою реализацию и покрыть её тестами.
Основными задачами для меня стали:
Так появился проект boolinq (название сочетает в себе слова bool и linq). Разместил его на Google Code: http://code.google.com/p/boolinq/ [3]. И вот что у меня получилось:
int sum = boolinq::from(cnadles).sum([](Candle c){return c.ClosePrice;});
Конечно, выглядит несколько сложнее LINQ. Но, это только за счёт синтаксиса лямбда-выражений в языке C++. Сама структура кода, осталось такой же. На данный момент реализованы следующие функции:
Преобразования последовательностей:
Аггрегаторы последовательностей:
Экспорт последовательности:
И даже несколько необычных:
Вот пример выражения:
int src[] = {1,2,3,4,5,6,7,8}; auto dst = from(src).where( [](int a){return a%2 == 1;}) // 1,3,5,7 .select([](int a){return a*2;}) // 2,6,10,14 .where( [](int a){return a>2 && a<12;}) // 6,10 .toVector();
К исходной коллекции пошагово применяются несколько операций:
1. Оставить только элементы с нечётным значением.
2. Умножить значение каждого из элементов на 2.
3. Оставить только элементы со значениями в диапазоне (2,12).
4. Результат поместить в std::vector
.
Или более сложное выражение:
struct Man { std::string name; int age; }; Man src[] = { {"Kevin",14}, {"Anton",18}, {"Agata",17}, {"Terra",20}, {"Layer",15}, }; auto dst = from(src).where( [](const Man & man){return man.age < 18;}) .orderBy([](const Man & man){return man.age;}) .select( [](const Man & man){return man.name;}) .toVector();
Тип у переменной dst
будет std::vector<std::string>
. Результирующий вектор будет содержать следующие значения: «Kevin», «Layer», «Agata». Применяемые к исходному массиву действия:
1. Оставить в массиве только людей моложе 18 лет.
2. Упорядочить элементы в массиве по увеличению возраста.
3. Выбрать из массива только имена.
4. Результат поместить в std::vector
.
В результате получилась библиотека отложенного выполнения запросов к массивам, векторам и другим контейнерам с данными. Скорость работы функций не уступает скорости работы аналогичной программы, написанной с использованием циклов. Синтаксис максимально приближен к LINQ. Я с удовольствием провёл время, проектируя и разрабатывая функционал библиотеки. Код неплохо покрыт тестами (не знаю сколько процентов, если кто подскажет — буду рад). Имеются функции, аналогов которых в LINQ — нет.
Распространяется библиотека в виде единого заголовочного-файла boolinq-all.h
. Буду рад, если кому-то библиотека окажется полезна. Если имеются предложения по улучшению, добавлению функции — прошу высказывайтесь. Если есть время и желание покодить — присоединяйтесь. Комментарии к коду на Google Code могут оставлять все желающие. Также создана группа для обсуждения на Google Groups: https://groups.google.com/forum/?fromgroups#!forum/boolinq [4]
Автор: k06a
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/6060
Ссылки в тексте:
[1] http://stackoverflow.com/questions/3221812/sum-of-elements-in-a-stdvector: http://stackoverflow.com/questions/3221812/sum-of-elements-in-a-stdvector
[2] Boost Ranges: http://www.boost.org/doc/libs/1_49_0/libs/range/doc/html/index.html
[3] http://code.google.com/p/boolinq/: http://code.google.com/p/boolinq/
[4] https://groups.google.com/forum/?fromgroups#!forum/boolinq: https://groups.google.com/forum/?fromgroups#!forum/boolinq
Нажмите здесь для печати.