This code snippet determines whether an integer is in a list called engie.
return std::find(engie.begin(), engie.end(), find_value) !=
engie.end();
Why is it an if statement comparing the iterator to the end of the list?
I realize the find range is from [first, last)
So for a list containing the integers {1,2,3}, isn't last = 3?
How does it find 3 if it isn't searched?
Input iterators to the initial and final positions in a sequence. The range searched is [first,last), which contains all the elements between first and last, including the element pointed by first but not the element pointed by last.
The function
find (InputIterator first, InputIterator last, const T& val) returns the position of the first match, if there is a match. Else it returns the last position. It will search in the range [first, last). This range includes the first element but not the last.
Hence in the code snippet when the iterator is not equal to the last element (i.e. engie.end) you get the match and the position of the match is returned.
You can refer this
std::find(b, e, v) searches for value v in half-open range [b,e), that is, excluding the last position e, and returns the first position where v is found, or e if v is not found in the range. So checking
std::find(b, e, v) != e
means, in words, "v is found in range [b,e)". You may think of std::find as
template<typename I, typename T>
I find(I b, I e, const T& v)
{
while (b != e && !(*b == v))
++b;
return b;
}
In the STL list {1,2,3}, end() does not point to 3, it points to one element past 3 or the "end" of the list. The find function iterates the list, and if the current iterator equals the value you're trying to find it returns the iterator. If it never finds the value it returns end() because one element past the last element in the list would be end().
The find method returns an iterator to the location (if found) or the value engie.end().
The comparison is simply converting the return value to true if found, false otherwise
Related
I've been experimenting with the "lower_bound()/upper_bound()" functions in C++ w.r.t. arrays/vectors, and I get incorrect results when applying custom compare operators to the function.
My current understanding (based on https://www.cplusplus.com/reference/algorithm/upper_bound/) is that when you search for some value 'val' (of any datatype) in an array, it returns the first iterator position "it" in the array (from left to right) that satisfies !comp(val,*it), is this wrong? If so, how exactly does the searching work?
P.S. In addition, what is the difference of using lowerbound/upperbound when your searching criterion is a specific boolean compare function?
Here is an example that produced erroneous results:
auto comp2 = [&](int num, pair<int,int>& p2){return num>p2.second;};
vector<pair<int,int>> pairs = {{1,2},{2,3},{3,4}}; //this array should be binary-searchable with 'comp2' comparator, since pairs[i].second is monotonously increasing
int pos2 = upper_bound(pairs.begin(),pairs.end(),2,comp2)-pairs.begin();
cout<<pos2<<endl; //outputs 3, but should give 0 because !comp2(2,arr[0]) is true, and arr[0] is the ealiest element in the array
Thanks!
I think most (If not all) of the comparator functions are less, it can be std::less or something similar. So when we provide a custom comp function, we have to provide the less logic and think of it as less.
Now back to the upper_bound, it returns the first element greater than the value, which means our less should return true for it to stop (As Francois pointed out). While our comp function always returns false.
And your understanding about !comp(val,*it) is also not correct. It is the condition to continue the search, not to stop it.
Here is an example implementation of the upper_bound, let's take a look:
template<class ForwardIt, class T, class Compare>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp)
{
ForwardIt it;
typename std::iterator_traits<ForwardIt>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);
if (!comp(value, *it)) {
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
You can see, if (!comp(value, *it)) is when the less return false, it means the value is greater than the current item, it will move forward and continue from the next item. (Because the items are increasing).
In the other case, it will try to reduce the search distance (By half the count) and hope to find earlier item that is greater than value.
Summary: You have to provide comp as less logic and let the upper_bound do the rest.
upper_bound returns the first element that satisfies comp(val, *it). In the link you provided, it shows
template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = std::distance(first,last);
while (count>0)
{
it = first; step=count/2; std::advance (it,step);
if (!(val<*it)) // or: if (!comp(val,*it)), for version (2)
{ first=++it; count-=step+1; }
else count=step;
}
return first;
}
Returns an iterator pointing to the first element in the range [first,last) which compares greater than val.
The searching works by starting at position 0(first). It then uses count to see the range of values it needs to check. It checks the middle of the range (first+count/2), and if that does not satisfy the condition, that position is now first (discarding all values before it), and repeats with the new first and range. If it does satisfy the condition, then the algorithm can discard all values after that, and repeat with the new range. When the range drops to 0, the algorithm can end. It assumes that if arr[5] is false, arr[0], arr[1] ... arr[4] are also false. Same with if arr[8] is true, arr[9], arr[10] ... arr[n] are also true.
The reason your code does not work is because the comparator used returns num>p2.second, meaning it looks for a value of p2.second that is less than num. Since you put in 2 for num, and there is no p2.second less than that in the vector, the output points to a position outside of the vector because it didn't find anything.
The difference between upper_bound and lower_bound is that upper_bound looks for the first value that satisfies the condition, while lower_bound looks for the first value that does not satisfy the condition. So
lower_bound(v.begin(), v.end(), val, [](int it, int val) {return !(val < it);});
is the same as
upper_bound(v.begin(), v.end(), val, [](int val, int it){return val < it;});
Note that for lower_bound, the comparator used takes (*it, val), not (val, *it).
I guess the only difference is how easy it is to frame the comparator in those terms - realizing that a<b is the same as not a>=b.
More explained here. I liked the explanation that said it finds [lower_bound, upper_bound) when using the same comparator.
vector<int> vec = {2,4,3};
vector<int>::iterator it;
it=lower_bound(vec.begin(),vec.end(),3);
cout<<*it;
This returns an output of 4 not 3 but
vector<int> vec = {2,3,4};
vector<int>::iterator it;
it=lower_bound(vec.begin(),vec.end(),3);
cout<<*it;
But this returns the correct output of 3. Please help me understand why it is failing in the corner case.
According to cppreference and its documentation of std::lower_bound:
Returns an iterator pointing to the first element in the range [first,
last) that is not less than (i.e. greater or equal to) value, or last
if no such element is found.
So, std::lower_bound returns the first element that is greater or equal to the value (3 here).
For {2, 4, 3}, the first element greater or equal to 3 is 4, but for {2, 3, 4,} it is 3.
P.S. According to the cppreference again:
The range [first, last) must be partitioned with respect to the
expression element < value or comp(element, value), i.e., all elements
for which the expression is true must precede all elements for which
the expression is false. A fully-sorted range meets this criterion.
Both of your vectors are partitioned correctly with the condition (element < value)
In short, the vector {2,4,3} doesn't meet the requirements of lower_bound.
https://en.cppreference.com/w/cpp/algorithm/lower_bound says:
The range [first, last) must be partitioned with respect to the
expression element < value or comp(element, value), i.e., all elements
for which the expression is true must precede all elements for which
the expression is false. A fully-sorted range meets this criterion.
If your vector is not sorted then use std::find.
I'm (forward) iterating over a std::map and would like to find if the iterator points to the second last element. I can't seem to find how to do that anywhere.
I've got:
bool
isSecondLastFile(const TDateFileInfoMap::const_iterator &tsFile)
{
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin() + 1;
return (tsFile == secondLastIt);
}
Where TDateFileInfoMap is std::map
I'm getting:
error: no match for ‘operator==’ in ‘tsFile == secondLastIt’
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_tree.h:287: note: candidates are: bool std::_Rb_tree_const_iterator<_Tp>::operator==(const std::_Rb_tree_const_iterator<_Tp>&) const [with _Tp = std::pair<const long int, TFileInfo>]
Does that mean I can't compare the forward and reverse iterator?
How do I figure out if the forward iterator is pointing at the second last element?
std::map's iterator type is BidirectionalIterator. Just decrement the end iterator twice--first to get the last element since m.end() returns an iterator at the after the end position, and then again to get the second-last element:
auto penultimate = std::prev(m.end(), 2);
Then you can simply check for equality with the resultant iterator:
auto it = m.begin();
it == penultimate;
see it live on Coliru
Naturally, you should check that the map has two elements first if it's not guaranteed by other logic in your program.
Does that mean I can't compare the forward and reverse iterator?
Yes you can't compare them directly.
You can use base() to get the underlying base iterator.
Returns the underlying base iterator. That is
std::reverse_iterator(it).base() == it.
The base iterator refers to the element that is next (from the
std::reverse_iterator::iterator_type perspective) to the element the
reverse_iterator is currently pointing to. That is &*(rit.base() - 1) == &*rit.
e.g.
return (tsFile == (++secondLastIt).base());
BTW: mFileInfoMap.rbegin() + 1 won't compile since the iterator of std::map is not RandomAccessIterator. You might write:
TDateFileInfoMap::reverse_iterator secondLastIt = mFileInfoMap.rbegin();
++secondLastIt;
Note that we're not checking whether the map is empty or has only one element.
A simple solution for forward iterators:
template <typename ForwardIterator>
inline bool isNthLast(std::size_t n, ForwardIterator pos, ForwardIterator last) {
for( ;; --n, ++pos) {
if(n == 0)
return (pos == last);
if(pos == last)
return false;
}
}
bool isSecondLastFile(TDateFileInfoMap::const_iterator sFile) {
return isNthLast(2, sFile, mFileInfoMap.end());
}
Let's say you have a set with name s.
s= {s1,s2,...,sN-1, sN}
Now to iterate from s1.. to sN-1 (which is second last element) we will use STL functions, s.begin() and s.end().
end = s.end(); //end points to end
end--// end points to sN
Now in the for loop when itr (starts from the beginning of set) becomes equal to sN the loop will break, and you will get s1,s2,..sN-1 inside the loop.
map<int,int> s;
// to iterate till fixed range in map
auto end =s.end();
end--; // end to second last;
for(auto itr = s.begin(); itr!=end;itr++){
// do your operation
}
This is the implementation as found on cplusplus.com
template <class InputIterator, class OutputIterator>
OutputIterator unique_copy (InputIterator first, InputIterator last,
OutputIterator result) {
if (first==last) return result;
*result = *first;
while (++first != last) {
typename iterator_traits<InputIterator>::value_type val = *first;
if (!(*result == val)) // or: if (!pred(*result,val)) for version (2)
*(++result)=val;
}
return ++result;
}
So when when "first" and "last" iterators point to the same element, we don't return anything? That seems out-of-line with unique_copy's definition: the first element from every consecutive group of equivalent elements in the range [first,last) is copied. Is it because of the "last)" part? Can anyone clarify? Thanks!
You are correct that the [first, last) is the problem.
When assigning iterators to containers, it is standard that the last iterator you can possible have is to the memory position 1 iteration after the last element in the container.
Ex.
vector<int> aVec{5,-8,23,200};
vector<int>::iterator currentItr, lastItr;
// iterator to first element, 5
currentItr = aVec.begin();
// iterator to element ***directly after*** last element
lastItr = aVec.end();
// in other words, there is no reason to access the value attached to lastItr
One reason why the .end() standard exists is to easily tell when the currentItr has reached past the usable values in a container;
Ex.
while (currentItr != lastItr) // currentItr will not print once it's equal to lastItr
cout << *currentItr++ << endl;
In your specific example, if there is one element in a container, then the iterators [first, last) should not be equal.
"first" is an iterator located one element to the left of iterator "last".
If first == last, then the span of possible iterators is:
[first, first)
or
[last, last)
The possible span cannot both include and exclude any iterator.
That sounds crazy.
Good luck.
I have a sorted vector and want to find a particular element in it. I can use binary_search for this but it only tells if it is present or not. I also need an iterator to access the element. Is there an easy way to this or I have to search it sequentially.
Any help appreciated.
Look into lower_bound and upper_bound. lower_bound gives the iterator to the first matching element while upper_bound gives the iterator one past the last matching element.
If either algorithm fails to find a match, it returns an iterator to the place where the item could be inserted to maintain a sorted container.
I've always felt binary_search was misleadingly named.
std::lower_bound will return the first element that is not less than your value. Meaning if the element returned is equal to your value your good, if it is not equal or the end iterator than the right element hasn't been found.
Here is the code from the dupe
template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
// Finds the lower bound in at most log(last - first) + 1 comparisons
Iter i = std::lower_bound(begin, end, val);
if (i != end && !(val < *i))
return i; // found
else
return end; // not found
}
Remember if you use std::upper_bound than it returns the first greater element so it is not as easy to adapt to your purposes because if your element is indeed found you have to decrement the iterator and even then you still may not find it