Я не уверен, что было бы справедливо подразумевать, что функциональное программирование могло предложить какие-либо преимущества перед императивным программированием с точки зрения производительности при работе с огромным количеством данных.
Я думаю, вам нужно добавить еще один инструмент в ваш инструментарий, и это может быть RxJS .
RxJS - это библиотека для составления асинхронных и событийных программ с использованием наблюдаемых последовательностей.
blockquote>Если вы не знакомы с RxJS или реактивным программированием в целом, мои примеры определенно будут выглядеть странно, но я думаю, что было бы неплохо познакомиться с этими концепциями [1114 ]
В вашем случае наблюдаемая последовательность - это ваш экземпляр MongoDB, который со временем генерирует записи.
Я собираюсь подделать вашу базу данных:
var db = range(1, 5);
Функция
range
- это функция RxJS, которая будет выдавать значение в указанном диапазоне.db.subscribe(n => { console.log(`record ${n}`); }); //=> record 1 //=> record 2 //=> record 3 //=> record 4 //=> record 5
Теперь меня интересуют только первая и последняя запись.
Я могу создать наблюдаемую, которая будет излучать только первую запись, и создать другую, которая будет излучать только последнюю:
var db = range(1, 5); var firstRecord = db.pipe(first()); var lastRecord = db.pipe(last()); merge(firstRecord, lastRecord).subscribe(n => { console.log(`record ${n}`); }); //=> record 1 //=> record 5
Однако мне также нужно обрабатывать все записи партиями: (в в этом примере я собираюсь создать партии по 10 записей в каждой)
var db = range(1, 100); var batches = db.pipe(bufferCount(10)) var firstRecord = db.pipe(first()); var lastRecord = db.pipe(last()); merge(firstRecord, batches, lastRecord).subscribe(n => { console.log(`record ${n}`); }); //=> record 1 //=> record 1,2,3,4,5,6,7,8,9,10 //=> record 11,12,13,14,15,16,17,18,19,20 //=> record 21,22,23,24,25,26,27,28,29,30 //=> record 31,32,33,34,35,36,37,38,39,40 //=> record 41,42,43,44,45,46,47,48,49,50 //=> record 51,52,53,54,55,56,57,58,59,60 //=> record 61,62,63,64,65,66,67,68,69,70 //=> record 71,72,73,74,75,76,77,78,79,80 //=> record 81,82,83,84,85,86,87,88,89,90 //=> record 91,92,93,94,95,96,97,98,99,100 //=> record 100
Как вы можете видеть в выводе, он выпустил:
- Первая запись
[ 118] Десять партий по 10 записей в каждой- Последняя запись
Я не буду пытаться решить ваше упражнение для вас, и я не слишком знаком с RxJS, чтобы расширять тоже много об этом.
Я просто хотел показать вам другой способ и дать вам понять, что это можно совместить с функциональным программированием.
Надеюсь, это поможет.
Я теперь нашел решение, которое более пригодно для моей исходной цели. Мне все еще не нравится он хотя :)
Решение вовлекает MagicIterator, шаблонный на IInterface* и создаваемый и с пустотой* к итератору, размер байта упомянутого итератора и таблица указателей на функции, которые выполняют стандартные итеративные функции на упомянутой пустоте*, такие как инкремент, декремент, разыменовывают и т.д. MagicIterator предполагает, что это безопасно к memcpy данный итератор во внутренний буфер и реализует его собственных участников путем передачи его собственного буфера как пустоты* к предоставленным функциям, как будто это был исходный итератор.
Контейнер затем должен реализовать статические итеративные функции, которые бросают предоставленную пустоту* к станд.:: список:: итератор. Контейнер:: начните () и Контейнер:: конец () просто создает станд.:: список:: итератор, передайте указатель на него в MagicIterator наряду с таблицей его итеративных функций и возвратите MagicIterator.
Это несколько отвратительно, и нарушает мое исходное правило относительно "никакого memcpy ()" и делает предположения о внутренностях рассматриваемых итераторов. Но это избегает выделения "кучи", сохраняет внутренности Набора (включая Объект) частными, MagicIterator рендеринга, совершенно независимый от рассматриваемого набора и от IInterface*, и в теории, позволяет MagicIterators работать с любым набором (если его итераторы могут быть безопасно memcopy () 'd).
Я думаю, что у Вас есть два отдельных вопроса здесь:
Во-первых, создайте итератор, который возвратится IInterface*
от Вашего list<Container::Item>
. Это, легко покончили boost::iterator_adaptor
:
class cont_iter
: public boost::iterator_adaptor<
cont_iter // Derived
, std::list<Container::Item>::iterator // Base
, IInterface* // Value
, boost::forward_traversal_tag // CategoryOrTraversal
, IInterface* // Reference :)
>
{
public:
cont_iter()
: cont_iter::iterator_adaptor_() {}
explicit cont_iter(const cont_iter::iterator_adaptor_::base_type& p)
: cont_iter::iterator_adaptor_(p) {}
private:
friend class boost::iterator_core_access;
IInterface* dereference() { return this->base()->pObject->GetInterface(); }
};
Вы создали бы этот тип как внутренний в Container
и возвратитесь в из begin()
и end()
методы.
Во-вторых, Вы хотите во время выполнения полиморфное MagicIterator
. Это точно что any_iterator
делает. MagicIterator<IInterface*>
справедливо any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>
, и cont_iter
может быть просто присвоен ему.
Не звучит слишком сложным. Можно определить итератор снаружи. Можно также использовать определения типов. Что-то вроде этого соответствовало бы, я думаю. Обратите внимание, что это был бы путь инструмент для очистки если, что MagicIterator будет не свободным шаблоном, а членом Объекта, typedefed в Контейнере, возможно. Поскольку это теперь, существует циклическая ссылка в нем, которые делают необходимым написать некоторый ужасный код обходного решения.
namespace detail {
template<typename T, typename U>
struct constify;
template<typename T, typename U>
struct constify<T*, U*> {
typedef T * type;
};
template<typename T, typename U>
struct constify<T*, U const*> {
typedef T const * type;
};
}
template<typename DstType,
typename Container,
typename InputIterator>
struct MagicIterator;
class Container
{
private:
struct Item
{
Object* pObject;
};
std::list<Item> m_items;
public:
// required by every Container for the iterator
typedef std::list<Item> iterator;
typedef std::list<Item> const_iterator;
// convenience declarations
typedef MagicIterator< IInterface*, Container, iterator >
item_iterator;
typedef MagicIterator< IInterface*, Container, const_iterator >
const_item_iterator;
item_iterator Begin();
item_iterator End();
};
template<typename DstType,
typename Container = Container,
typename InputIterator = typename Container::iterator>
struct MagicIterator :
// pick either const T or T, depending on whether it's a const_iterator.
std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> {
typedef std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> base;
MagicIterator():wrapped() { }
explicit MagicIterator(InputIterator const& it):wrapped(it) { }
MagicIterator(MagicIterator const& that):wrapped(that.wrapped) { }
typename base::value_type operator*() {
return (*wrapped).pObject->GetInterface();
}
MagicIterator& operator++() {
++wrapped;
return *this;
}
MagicIterator operator++(int) {
MagicIterator it(*this);
wrapped++;
return it;
}
bool operator==(MagicIterator const& it) const {
return it.wrapped == wrapped;
}
bool operator!=(MagicIterator const& it) const {
return !(*this == it);
}
InputIterator wrapped;
};
// now that the iterator adepter is defined, we can define Begin and End
inline Container::item_iterator Container::Begin() {
return item_iterator(m_items.begin());
}
inline Container::item_iterator Container::End() {
return item_iterator(m_items.end());
}
Теперь, начните использовать его:
for(MagicIterator<IInterface*> it = c.Begin(); it != c.End(); ++it) {
// ...
}
Можно также использовать смешивание итератора, обеспеченное повышением, которое работает как входная версия повышения:: function_output_iterator. Это называет Ваш итератор operator()
который затем возвращает соответствующее значение, делая то, что мы делаем выше в нашем operator*
в принципе. Вы находите его в random/detail/iterator_mixin.hpp
. Это, вероятно, привело бы к меньшему количеству кода. Но это также требует для разрушения нашей шеи для обеспечения материала друга, потому что Объект является частным, и итератор не определяется в Объекте. Так или иначе, удача :)
Создайте краткий обзор IteratorImplementation
класс:
template<typename T>
class IteratorImplementation
{
public:
virtual ~IteratorImplementation() = 0;
virtual T &operator*() = 0;
virtual const T &operator*() const = 0;
virtual Iterator<T> &operator++() = 0;
virtual Iterator<T> &operator--() = 0;
};
И Iterator
класс для переноса его:
template<typename T>
class Iterator
{
public:
Iterator(IteratorImplementation<T> * = 0);
~Iterator();
T &operator*();
const T &operator*() const;
Iterator<T> &operator++();
Iterator<T> &operator--();
private:
IteratorImplementation<T> *i;
}
Iterator::Iterator(IteratorImplementation<T> *impl) :
i(impl)
{
}
Iterator::~Iterator()
{
delete i;
}
T &Iterator::operator*()
{
if(!impl)
{
// Throw exception if you please.
return;
}
return (*impl)();
}
// etc.
(Можно сделать IteratorImplementation
класс "в" Iterator
сохранять вещи опрятными.)
В Вашем Container
класс, возвратите экземпляр Iterator
с пользовательским подклассом IteratorImplementation
в ctor
:
class ObjectContainer
{
public:
void insert(Object *o);
// ...
Iterator<Object *> begin();
Iterator<Object *> end();
private:
class CustomIteratorImplementation :
public IteratorImplementation<Object *>
{
public:
// Re-implement stuff here.
}
};
Iterator<Object *> ObjectContainer::begin()
{
CustomIteratorImplementation *impl = new CustomIteratorImplementation(); // Wish we had C++0x's "var" here. ;P
return Iterator<Object *>(impl);
}
Я не вижу оснований, почему Вы не можете реализовать это точно, поскольку Вы разметили его... я пропускающий что-то?
Для разъяснения необходимо будет поместить некоторые методы доступа для Контейнерного класса. Они могут быть частными, и можно объявить MagicIterator как друг, если бы Вы чувствуете, что это - лучший способ инкапсулировать его, но я выставил бы их непосредственно. Эти методы доступа использовали бы нормальный итератор STL в Контейнере и выполнили бы преобразование в IInterface. Таким образом итерация была бы на самом деле сделана с методами доступа Контейнера, и MagicIterator просто будет своего рода объектом прокси помочь. Для создания этого повторно используемым Вы могли сделать, чтобы MagicIterator передал в некотором идентификаторе для поиска итератора STL в Контейнере, или у Вас мог на самом деле быть он передача в итераторе STL как a void *
.
Это действительно зависит от Container
, потому что возвращаемые значения c.Begin()
и c.End()
определяются реализацией.
Если список возможных Container
s известен MagicIterator
, класс обертки мог использоваться.
template<typename T>
class MagicIterator
{
public:
MagicIterator(std::vector<T>::const_iterator i)
{
vector_const_iterator = i;
}
// Reimplement similarly for more types.
MagicIterator(std::vector<T>::iterator i);
MagicIterator(std::list<T>::const_iterator i);
MagicIterator(std::list<T>::iterator i);
// Reimplement operators here...
private:
std::vector<T>::const_iterator vector_const_iterator;
std::vector<T>::iterator vector_iterator;
std::list<T>::const_iterator list_const_iterator;
std::list<T>::iterator list_iterator;
};
Простой способ состоял бы в том, чтобы использовать шаблон, который принимает Container
тип:
// C++0x
template<typename T>
class Iterator :
public T::iterator
{
using T::iterator::iterator;
};
for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
{
// ...
}
Посетитель может быть более простым (и, следовательно, более удобным в обслуживании) решением.