2009-07-01 3 views
2

Я занимаюсь кодированием при работе на C++, и многое из того, что я работаю, связано с анализом наборов данных. Очень часто мне нужно выбрать некоторые элементы из STL контейнера, и очень часто я написал такой код:SQL-Like Selects in Imperative Languages ​​

using std::vector; 
vector<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.push_back(i); 
} 

vector<int> positive_numbers; 
for (vector<int>::const_iterator it = numbers.begin(), end = numbers.end(); 
     it != end; ++it 
) { 
    if (number > 0) { 
     positive_numbers.push_back(*it); 
    } 
} 

Со временем это цикл и логики, содержащейся в нем становится намного более сложным и нечитаемым. Код, как это меньше удовлетворения, чем аналогичная ЗЕЬЕСТ в SQL, при условии, что у меня есть таблица под названием номера с колонкой с именем «номер», а не станд :: вектор < Int>:

SELECT * INTO positive_numbers FROM numbers WHERE num > 0 

Это много более читаемый для меня, а также масштабируется лучше, со временем большая логика if-statement, которая находится в нашей кодовой базе, стала сложной, зависящей от заказов и не поддающейся контролю. Если бы мы могли делать SQL-подобные инструкции на C++ без необходимости обращаться к базе данных, я думаю, что состояние кода может быть лучше.

Есть ли более простой способ реализовать что-то вроде инструкции SELECT в C++, где я могу создать новый контейнер объектов, только описывая характеристики объектов, которые я хочу? Я все еще относительно новичок в C++, поэтому я надеюсь, что есть что-то волшебное с метапрограммированием шаблонов или умными итераторами, которые разрешат это. Благодаря!

Редактировать на основе первых двух ответов. Спасибо, я понятия не имел, что на самом деле LINQ. Я программирую в основном на Linux и OSX-системах, и меня интересует нечто кросс-платформенное в OSX, Linux и Windows. Таким образом, более образованная версия этого вопроса будет - есть ли кросс-платформенная реализация чего-то типа LINQ для C++?

ответ

2

LINQ является очевидным ответом на .NET (или Mono на не-Windows-платформ, но в C++, это не должно быть так сложно написать что-то вроде это самостоятельно в STL.

Используйте Boost.Iterator библиотека написать «выбрать» итератор, например, один, который пропускает все элементы, которые не удовлетворяют данный предикат.

подталкивания уже есть несколько соответствующих примеров в их документации, я считаю. Или http://www.boost.org/doc/libs/1_39_0/libs/iterator/doc/filter_iterator.html действительно может сделать то, что вы необходимо из коробки.

В любом случае, на C++, вы coul d достигают такого же эффекта, в основном, с помощью итераторов.

Если у вас есть обычный итератор, который посещает каждый элемент последовательности, вы можете обернуть это в итераторе фильтра, который увеличивает исходный итератор до тех пор, пока не найдет значение, удовлетворяющее условию. Тогда вы можете даже обернуть это в «select» итераторе, преобразуя значение в желаемый формат.

Это кажется довольно очевидной идеей, но я не знаю никаких полных ее реализаций.

+0

Спасибо jalf. Я искал что-то волшебное, которое уже было реализовано для этого, но я, скорее всего, попробую ваше предложение с помощью итераторов и фильтров. –

3

Я думаю, вы описали LINQ (функция C# и .NET 3.5). Вы изучили это?

+0

Это особенность .NET, поэтому его можно использовать из C++, я предполагаю. –

4

Вы почти точно описали LINQ. Это функция .NET 3.5, поэтому вы можете использовать ее на C++.

4

Функциональность вы описываете обычно находится в функциональных языках, которые поддерживают такие понятия, как замыкания, предикаты, функторов и т.д.

Проблема с выше кода является то, что он сочетает в себе:

  1. Логика для итерации по коллекции (цикл for)
  2. Условие, которое должно быть выполнено для элемента, подлежащего копированию в другую коллекцию
  3. Логика для копирования элемента из одной коллекции в другую

В действительности (1) и (3) являются шаблонами, поскольку каждый раз, когда вам нужно перебирать коллекцию, копируя некоторые элементы в другую коллекцию, это, вероятно, только условный код, который будет меняться каждый раз. Языки с поддержкой функционального программирования исключают этот шаблон. Например, в Groovy можно заменить цикл выше всего

def positive_numbers = numbers.findAll{it > 0} 

Несмотря на C++ не является функциональным языком могут быть библиотеки, которые обеспечивают поддержку для программирования функционального стиля с коллекциями STL. Например, коллекция коллекций Apache (а также, возможно, библиотека коллекции Google) обеспечивает поддержку программирования функционального стиля с коллекциями Java, хотя сама Java не является функциональным языком.

0

Проверьте Mono, если вы хотите попробовать LINQ на Linux/OS X. Это порт .NET Framework и LINQ включен сейчас, я верю.

1

Вы используете контейнеры STL. Я бы рекомендовал использовать STL algorithms, которые в значительной степени прямо из теории множеств. Выбор SQL переводится в повторные приложения std::find_if или комбинации std::lower_bound и std::upper_bound (на сортированных контейнерах). Производительность будет примерно такой же, как и цикл, но синтаксис является немного более декларативным.

LINQ предоставит вам аналогичный синтаксис и операции, но если вы не используете более IQueryable (т. Е. Данные в базе данных), вы также не получите никакой выгоды от производительности.

Ваш лучший выбор после этого вставляет вещи в файлы для такого рода вещей. Является ли это BerkelyDB, NetCDF, HDF5, STXXL и т. Д. Доступ к файлам выполняется медленно, но это позволяет работать с большим количеством данных, чем подходит в памяти.

1

Для того, что вы описываете, std :: vector - не очень хороший выбор. Это эквивалент SQL для таблицы без индексов. Кроме того, заполнение одного контейнера содержимым другого контейнера, возможно, является разумной оптимизацией производительности, но не очень читаемо и не совсем идиоматично. Существует несколько способов решения этой задачи (IE, не полагаясь на управляемый код .net).

Первый выбор - выбрать лучший контейнер. Если вам не нужна стабильная итерация, вам следует использовать std :: set или std :: multi_set. эти контейнеры используют сбалансированное дерево поиска для сохранения значений по порядку. Это эквивалентно простому индексу SQL всех столбцов.

std::set<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.insert(i); 
} 

std::set::iterator first = numbers.find(1); 
std::set::iterator end = numbers.end(); 

Теперь вы можете перемещаться от first до end, не тратя дополнительные усилия, над O (п журнал (п)) заливки и O (журнал (п)) искать.Итерация является O (1) для std::set::iterator

Если по какой-то причине вы должны использовать вектор, вы можете получить более идиоматических C++ с использованием std::find_if (см Max Lybbert's ответ)

bool isPositive(int n) { return n > 0; } 

std::vector<int> numbers; 
for (int i = -10; i <= 10; ++i) { 
    numbers.push_back(i); 
} 

for (std::vector<int>::const_iterator end = numbers.end(), 
     iter = std::find_if(numbers.begin(), end, isPositive); // <- first positive value 
     iter != end; 
     iter = std::find_if(iter, end, isPositive) // <- advance iter to the next positive 
) { 

    // iter is guaranteed to be positive here, do something with it! 
} 

Если вы хотите что-то еще более запоминающийся SQL без фактического подключения к базе данных, вы должны посмотреть на Boost, в частности на контейнер boost::multi_index и увеличить итераторы.

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