New form of equal_range()

  • Follow


Hi

// Previously posted by myself on microsoft.public.vc.stl as a variant

Say I have vector<int>, it is sorted, there maybe duplicates and
I wish to find the range [begin, end) of int 75.

I can do

vector<int>    v;
pair<vector<int>::iterator, vector<int>::iterator> range;
vector<int>::iterator it1, it2;

// populate vector here

range = equal_range(v.begin(), v.end(), 75);

and now the iterator range [range.first, range.second) is a STL range to any
instances of 75.
So far, so good.

But now suppose I wish to find the range [51, 100). Now what?
It seems I can only do this in 2 steps:

it1 = lower_bound(v.begin(), v.end(), 51);
it2 = upper_bound(v.begin(), v.end(), 100);

I can't do this using equal_range(), or can I?

It is a shame there is not

range = equal_range(v.begin(), v.end(), object1, object2);
range = equal_range(v.begin(), v.end(), object1, object2, predicate());

as part of the library where as a precondition, either
object1 < object2
or
!predicate(object2, object1)
is true.

Thanks

Stephen Howe




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stephen 1/26/2005 9:52:21 AM

Stephen Howe wrote:
>Say I have vector<int>, it is sorted, there maybe duplicates and
>I wish to find the range [begin, end) of int 75.
>
>I can do
....
>
>range = equal_range(v.begin(), v.end(), 75);
>
>and now the iterator range [range.first, range.second) is a STL range to any
>instances of 75.
>So far, so good.
>
>But now suppose I wish to find the range [51, 100). Now what?

You can define a predicate that compares to a range instead of a
specific value.

class range_pred {
	typedef std::pair<int, int> data;
public:
	bool operator()(data d, int val) const
	{
		return val < d.first || val > d.second;
	}
	
	bool operator()(int val, data d) const
	{
		return (*this)(d, val);
	}
};

// and use it this way
range = equal_range(v.begin(), v.end(), 
					std::make_pair(51, 100), range_pred());

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Motti 1/26/2005 6:59:15 PM


Stephen Howe wrote:
> <snip>
>
> But now suppose I wish to find the range [51, 100). Now what?
> It seems I can only do this in 2 steps:
> 
> it1 = lower_bound(v.begin(), v.end(), 51);
> it2 = upper_bound(v.begin(), v.end(), 100);
> 
> I can't do this using equal_range(), or can I?
> 

You can do it be writing your own predicate, basically write it so that 
it treats all numbers in the range [51,100) as equal.

something like (note: I haven't tried compiling this.. sorry!)
template<int lower, int upper>
struct range_predicate {
bool operator()(int i,int j) {
if(lower<=i && i<upper && lower<=j && j<upper) return false;
return i<j;
}
};

then do
range = equal_range(v.begin, v.end(), range_predicate<50,100>());

Although I will admit this isn't as nice as it could be...

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Bob 1/26/2005 7:22:56 PM

Stephen Howe wrote:
> Hi
....
> range = equal_range(v.begin(), v.end(), 75);
>
> and now the iterator range [range.first, range.second) is a STL range
to any
> instances of 75. So far, so good.
>
> But now suppose I wish to find the range [51, 100). Now what?
> It seems I can only do this in 2 steps:
>
> it1 = lower_bound(v.begin(), v.end(), 51);
> it2 = upper_bound(v.begin(), v.end(), 100);
>
> I can't do this using equal_range(), or can I?

No, and it seems rather obvious to me. 51 is not equal to 100, and
*it1 is not equal to *it2.

BTW, try
it1 = lower_bound(v.begin(), v.end(), 51);
it2 = upper_bound(it1, v.end(), 100);

No need to search for the value 100 in the range [v.begin(), it1]
HTH,
Michiel Salters


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply msalters 1/26/2005 7:31:27 PM

"Stephen Howe" <sjhoweATdialDOTpipexDOTcom@eu.uu.net> wrote in message 
news:41f7170a$0$311$cc9e4d1f@news.dial.pipex.com...

> It is a shame there is not
>
> range = equal_range(v.begin(), v.end(), object1, object2);
> range = equal_range(v.begin(), v.end(), object1, object2, predicate());
>
> as part of the library where as a precondition, either
> object1 < object2
> or
> !predicate(object2, object1)
> is true.

Why?  Is that really such a common operation?  Is it so difficult to write

    it1 = lower_bound(v.begin(), v.end(), object1);
    it2 = upper_bound(it1, v.end(), object2);

?

If there were a form of equal_range that took two objects, would you also 
want one that took three?


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrew 1/26/2005 8:51:39 PM

> If there were a form of equal_range that took two objects, would you also
> want one that took three?

No. It is the fact that 2 objects searched for would be the limits of an stl
range. What would it mean with 3 objects?
But I guess this is non-standard as no other algorithm takes a pair of
objects.

The other reason for asking is the the complexity requirements for such an
equal_range() might be lower than lower_bound() combined with upper_bound().
I have not sat and analysed this yet.

Stephen Howe



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stephen 1/27/2005 12:52:05 PM

On 26 Jan 2005 04:52:21 -0500, "Stephen Howe"
<sjhoweATdialDOTpipexDOTcom@eu.uu.net> wrote:

> Say I have vector<int>, it is sorted, there maybe duplicates and

> But now suppose I wish to find the range [51, 100). Now what?
> It seems I can only do this in 2 steps:

> it1 = lower_bound(v.begin(), v.end(), 51);
> it2 = upper_bound(v.begin(), v.end(), 100);

This gives you [51, 100].  If you really want [51, 100) you need
lower_bound for the second also.

> I can't do this using equal_range(), or can I?

Yes you can.  It needs a predicate and careful use.  I assume a
range of the form [,).

template <class T>
struct Comp {
     T b, e;
     Comp (T b, T e) : b(b), e(e) {
         assert(b <= e);
         }
     bool operator() (T lhs, T rhs) {
         return lhs == b ? ! (rhs < e) : lhs < b;
         }
     };

std::equal_range(v.begin(), v.end(), 51, Comp<int>(51, 100));

Why not just write the algorithm you want?

template <class I, class T>
std::pair<I, I> equal_range(I first, I past, T b, T e) {
     I f(std::lower_bound(first, past, b));
     return std::pair<I, I>(f, std::lower_bound(f, past, e));
     }

It does seem to be less complicated.

If you really think the combined search of std::equal_range
will be significantly better, you could just code it with
the Comp.

template <class I, class T>
std::pair<I, I> equal_range(I first, I past, T b, T e) {
     return std::equal_range(first, past, b, Comp<T>(b, e));
     }

John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply John 1/27/2005 12:52:45 PM

> BTW, try
> it1 = lower_bound(v.begin(), v.end(), 51);
> it2 = upper_bound(it1, v.end(), 100);

Yes. That occured to me after I posted.
It reduces the compares.

Thanks

Stephen Howe




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stephen 1/27/2005 12:54:39 PM

"Andrew Koenig" <ark@acm.org> writes:

> "Stephen Howe" <sjhoweATdialDOTpipexDOTcom@eu.uu.net> wrote in message
> news:41f7170a$0$311$cc9e4d1f@news.dial.pipex.com...
>
>> It is a shame there is not
>>
>> range = equal_range(v.begin(), v.end(), object1, object2);
>> range = equal_range(v.begin(), v.end(), object1, object2, predicate());
>>
>> as part of the library where as a precondition, either
>> object1 < object2
>> or
>> !predicate(object2, object1)
>> is true.
>
> Why?  Is that really such a common operation?  Is it so difficult to write
>
>     it1 = lower_bound(v.begin(), v.end(), object1);
>     it2 = upper_bound(it1, v.end(), object2);
>
> ?
>
> If there were a form of equal_range that took two objects, would you also
> want one that took three?

I'm not sure what that would mean, but the semantics of the two-object
case are achievable now that we've made heterogeneous comparison
operators legal with equal_range et. al.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 1/27/2005 12:55:23 PM

On 26 Jan 2005 13:59:15 -0500, Motti Lanzkron <mlanzkron@yahoo.co.uk>
wrote:

> Stephen Howe wrote:

> >But now suppose I wish to find the range [51, 100). Now what?

> You can define a predicate that compares to a range instead of a
> specific value.

> class range_pred {
>       typedef std::pair<int, int> data;
> public:
>       bool operator()(data d, int val) const
>       {
>               return val < d.first || val > d.second;
>       }
>
>       bool operator()(int val, data d) const
>       {
>               return (*this)(d, val);
>       }
> };

> // and use it this way
> range = equal_range(v.begin(), v.end(),
>                                       std::make_pair(51, 100), range_pred());

Not bad, but try compiling it with a fussy compiler like comeau.
Hint:

    std::vector<unsigned> v;
    std::equal_range(v.begin(), v.end(), 42);

fails.

John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply John 1/27/2005 12:56:30 PM

Motti Lanzkron wrote:
> Stephen Howe wrote:
>
>>Say I have vector<int>, it is sorted, there maybe duplicates and
>>I wish to find the range [begin, end) of int 75.
>>
>>I can do
>
> ....
>
>>range = equal_range(v.begin(), v.end(), 75);
>>
>>and now the iterator range [range.first, range.second) is a STL range to any
>>instances of 75.
>>So far, so good.
>>
>>But now suppose I wish to find the range [51, 100). Now what?
>
>
> You can define a predicate that compares to a range instead of a
> specific value.
>
> class range_pred {
>       typedef std::pair<int, int> data;
> public:
>       bool operator()(data d, int val) const
>       {
>               return val < d.first || val > d.second;
>       }
>
>       bool operator()(int val, data d) const
>       {
>               return (*this)(d, val);
>       }
> };
>
> // and use it this way
> range = equal_range(v.begin(), v.end(),
>                     std::make_pair(51, 100), range_pred());

Shouldn't that be:

class range_pred {
        typedef std::pair<int, int> data;
public:
        bool operator()(data d, int val) const
        {
                return val > d.second;
        }

        bool operator()(int val, data d) const
        {
                return val < d.first;
        }
};

After all, the predicate is testing less-than, not containment.

While I consider this to be the best solution, I do not see how it is
portable.  equal_range requires the container to be considered sorted
with the predicate specified, and this predicate can not be applied to
the container!

   -- MJF

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply M 1/27/2005 1:07:47 PM

I would consider std::subrange(It1,It2,lower,upper) much less esoteric
than, say, random_shuffle, even though I agree that it is not so
terrible to have to do it in two statements.

Cheers,
Nicola Musatti


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Nicola 1/27/2005 8:48:43 PM

John Potter wrote:
>
>Not bad, but try compiling it with a fussy compiler like comeau.
>Hint:
>
>    std::vector<unsigned> v;
>    std::equal_range(v.begin(), v.end(), 42);
>
>fails.

I'm looking at the standard now and I see: 

template<class ForwardIterator, class T>
pair<ForwardIterator, ForwardIterator>
equal_range(ForwardIterator, ForwardIterator, const T&);

Where does it require that T be the same type as *ForwardIterator ?

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Motti 1/27/2005 8:56:05 PM

>> it1 = lower_bound(v.begin(), v.end(), 51);
>> it2 = upper_bound(v.begin(), v.end(), 100);
>
> This gives you [51, 100].  If you really want [51, 100) you need
> lower_bound for the second also.

Thanks John. The code is right, my maths notation is wrong. I should have
[51, 100+1). But I guess on thinking that the 2nd object should be 
non-inclusive limit so that it matches the general form of ranges in the 
library.

Stephen Howe 



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Stephen 1/27/2005 8:58:18 PM

John Potter <jpotter@lhup.edu> writes:

> Not bad, but try compiling it with a fussy compiler like comeau.
> Hint:
>
>     std::vector<unsigned> v;
>     std::equal_range(v.begin(), v.end(), 42);
>
> fails.

But Comeau is now officially "too fussy."
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#270

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply David 1/27/2005 9:30:46 PM

On 27 Jan 2005 16:30:46 -0500, David Abrahams
<dave@boost-consulting.com> wrote:

> John Potter <jpotter@lhup.edu> writes:

> > Not bad, but try compiling it with a fussy compiler like comeau.
> > Hint:
> >
> >     std::vector<unsigned> v;
> >     std::equal_range(v.begin(), v.end(), 42);

> > fails.

> But Comeau is now officially "too fussy."
> http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#270

Status WP.  Comeau will be "too fussy" when and if the WP becomes
a standard with those words still intact.  I wish I could file a
bug report on Comeau today.

John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply John 1/28/2005 12:36:40 AM

On 27 Jan 2005 15:56:05 -0500, Motti Lanzkron <mlanzkron@yahoo.co.uk>
wrote:

> John Potter wrote:

> >Not bad, but try compiling it with a fussy compiler like comeau.
> >Hint:

> >    std::vector<unsigned> v;
> >    std::equal_range(v.begin(), v.end(), 42);

> >fails.

> I'm looking at the standard now and I see: 

> template<class ForwardIterator, class T>
> pair<ForwardIterator, ForwardIterator>
> equal_range(ForwardIterator, ForwardIterator, const T&);

> Where does it require that T be the same type as *ForwardIterator ?

It seems to be indicated in 25.3.3/1.  Some think that the following
is a valid implementation.

template <class T>
struct __less {
   bool operator() (T const& lhs, T const& rhs) {
      return lhs < rhs;
      }
   };
template <class FI, class T>
pair<FI, FI> equal_range (FI f, FI p, T const& v) {
   return equal_range(f, p, v, __less<T>());
   }

Some also think that had things been better understood, it would
have been

template <class FI>
pair<FI, FI> equal_range(FI, FI, typename
      iterator_traits<FI>::value_type const&);

That, of course is all history.  The only important question today
is whether you want your code to compile today on fussy compilers.

John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply John 1/28/2005 12:37:47 AM

Stephen Howe wrote:

>>> it1 = lower_bound(v.begin(), v.end(), 51);
>>> it2 = upper_bound(v.begin(), v.end(), 100);
>>
>> This gives you [51, 100].  If you really want [51, 100) you
>> need lower_bound for the second also.
> 
> Thanks John. The code is right, my maths notation is wrong. I
> should have [51, 100+1). But I guess on thinking that the 2nd
> object should be non-inclusive limit so that it matches the
> general form of ranges in the library.

Past-the-end iterators are supposed always to exist, but what is past 
the end of int's range?

-- 
Quidquid latine dictum sit, altum viditur.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Martin 1/29/2005 4:55:25 AM

On 28 Jan 2005 23:55:25 -0500, Martin Eisenberg
<martin.eisenberg@udo.edu> wrote:

> Stephen Howe wrote:

> >>> it1 = lower_bound(v.begin(), v.end(), 51);
> >>> it2 = upper_bound(v.begin(), v.end(), 100);

> >> This gives you [51, 100].  If you really want [51, 100) you
> >> need lower_bound for the second also.

> > Thanks John. The code is right, my maths notation is wrong. I
> > should have [51, 100+1). But I guess on thinking that the 2nd
> > object should be non-inclusive limit so that it matches the
> > general form of ranges in the library.

> Past-the-end iterators are supposed always to exist, but what is past 
> the end of int's range?

The choice is either to pick one or use [,] preventing an empty range.
A very old problem for full range of anything.  do-while for complete
range of any integral type prevents zero executions.  You can't have
both.

John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply John 1/29/2005 3:29:34 PM

John Potter wrote:

> On 28 Jan 2005 23:55:25 -0500, Martin Eisenberg
> <martin.eisenberg@udo.edu> wrote:

>> Past-the-end iterators are supposed always to exist, but what
>> is past the end of int's range?
> 
> The choice is either to pick one or use [,] preventing an empty
> range. A very old problem for full range of anything.  do-while
> for complete range of any integral type prevents zero
> executions.  You can't have both.

What about the mathematical convention that sum_{i=m}^n a_i = 0
for m > n? The programming analog would break down for a type with 
just one value, oh well. (I acknowledge that there is another 
convention making the sum zero only for m = n + 1.)

-- 
Quidquid latine dictum sit, altum viditur.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Martin 2/3/2005 9:43:03 PM

19 Replies
197 Views

(page loaded in 0.246 seconds)


Reply: