|
Boost : |
From: Arno Schödl (aschoedl_at_[hidden])
Date: 2008-09-02 02:47:34
> Enough for today. Maybe tomorrow I'll get some idea.
Let me try:
First of all, associated_range has the semantics of "[efficiently (who wouldn't want that?)] holds pair of iterators of certain type". This is the same semantics as iterator_range. The existing iterator_ranges are the default implementation of associated_range, and the associate_ranges we want to implement are specialized iterator_ranges for certain types of iterators. The two types are the same. So let's use this iterator_range throughout, since we already have it.
To eliminate quadratic running times, we must eliminate any linear-space expansion of function parameters. To do this, let's introduce some helper functions that make an iterator_range look like an iterator. Since we just discovered the dual life of iterator_range as associated_range, I will add them to iterator_range:
template< class I >
class iterator_range {
...other iterator_range stuff...
// regular copy ctor for begin iterator
////////////////////////////////////
// associated_range interface
// end iterator ctor
iterator_range( iterator_range const& range, create_end_tag )
: m_begin( range.m_end ), m_end( range.m_end )
{}
// ctor from pair of ranges
iterator_range( iterator_range const& b, iterator_range const& e )
: m_begin( b.m_begin ), m_end( e.m_begin )
{}
// the names are chosen to reflect the function in iterators
// they are meaningless when interpreted in range terms
void increment() { // could be called "transform_to_rest" or simply "rest"?
++m_begin;
}
// TODO: check traversal_category whether this is allowed
// Not needed by the forward filter_iterator below, but
// would be used by bidirectional filter_iterator.
void decrement() { // but what do we call this then?
--m_begin;
}
// The three following helpers are optional.
// [actually I am not so sure, I have not tried to eliminate them]
// They could be implented via
// begin/end, but begin/end is still O(N) because it needs to copy the whole stack.
// If the compiler can optimize away trivial forwards, these functions
// are O(1):
// TODO: do we have to do anything about constness?
reference dereference() const { // could be called "front"
return *m_begin;
}
bool equal( iterator_range const& range ) const {
return m_begin==range.m_begin;
}
bool at_end() const {
return m_begin==m_end;
}
// TODO: advance, distance_to if traversal_category.
// I first thought we never need them, but my company has a concat_range
// that probably needs them. Would have to look in detail...
};
Now we implement iterator_range on filter_iterator:
template< class I, class F > class filter_iterator<I,F>;
template< class I, class F >
class iterator_range< filter_iterator<I,F> > {
private:
iterator_range<I> m_range;
F m_f;
void skip_filter() {
while( !m_range.at_end() && !m_f( m_range.dereference() ) ) {
m_range.increment();
}
}
public:
////////////////////////////////////
// associated_range interface
// end iterator ctor
iterator_range( iterator_range const& range, create_end_tag )
: m_range( range.m_range, create_end_tag() ), m_f( range.m_f )
{}
// ctor from pair of ranges
iterator_range(iterator_range<I,F> const& b, iterator_range<I,F> const& e )
: m_range( b.m_range, e.m_range ), m_f( b.m_f )
{}
// the names are chosen to reflect the function in iterators
// they are meaningless when interpreted in range terms
void increment() {
m_range.increment();
skip_filter();
}
// TODO: decrement to be bidirectional
// TODO: do we have to do anything about constness?
reference dereference() const {
return m_range.dereference();
}
bool equal( iterator_range const& range ) const {
return m_range.equal( range.m_range );
}
////// end of associated_range interface /////
// ctor from underlying range
iterator_range(iterator_range<I> const& range, F f )
: m_range( range.m_range ), m_f( f )
{
skip_filter();
}
// ctor from underlying iterators
iterator_range(I const& b, I const& e, F const& f )
: m_range( b, e ), m_f( f )
{
skip_filter();
}
// ctor from own iterators
iterator_range(filter_iterator<I,F> const& b, filter_iterator<I,F> const& e )
: m_range( b.m_range.m_range, e.m_range.m_range ), m_f( b.m_range.m_f )
{}
// copy ctor
iterator_range( iterator_range const& range )
: m_range( range.m_range ), m_f( range.m_f )
{}
filter_iterator<I,F> begin() const {
return filter_iterator<I,F>( *this );
}
filter_iterator<I,F> end() const {
return filter_iterator<I,F>( *this, create_end_tag );
}
};
The filter_iterator is reduced to a mere shell around the range:
template< class I, class F >
class filter_iterator<I,F> {
typedef iterator_range< filter_iterator<I,F> > myrange_t;
myrange_t m_range;
// copy ctor trivial
// ctor from range
filter_iterator( myrange_t const& range )
: m_range( range )
{}
// ctor from end range
filter_iterator( myrange_t const& range, create_end_tag )
: m_range( range, create_end_tag() )
{}
void increment() {
m_range.increment();
}
reference dereference() const {
return m_range.dereference();
}
bool equal( filter_iterator const& it ) const {
return m_range.equal( it.m_range );
}
};
If you are uncomfortable with the design because you don't see the connection to what you [Giovanni] proposed earlier: I believe that I really made little active choices in the design. I merely eliminated long parameter lists that give quadratic running times by pushing the parameters through the stack, through all the other functors, all the way into the base case.
-- Dr. Arno Schoedl · aschoedl_at_[hidden] Technical Director think-cell Software GmbH · Invalidenstr. 34 · 10115 Berlin, Germany http://www.think-cell.com · phone +49-30-666473-10 · toll-free (US) +1-800-891-8091 Directors: Dr. Markus Hannebauer, Dr. Arno Schoedl · Amtsgericht Charlottenburg, HRB 85229
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk