Home > Mobile >  C decrementing the iterator
C decrementing the iterator

Time:09-30

I'm new to C and currently learning iterators.

I wrote the following code, which adds the first and last digit in a vector.

In order to decrement the iterator, I've had to decrement the variable dec_pointer twice for the correct results. I'm obviously doing something wrong, but what?

#include <iostream>
#include <vector>
#include <string>

int main()
{
    std::vector<int> vec{1,4,2,6,9,10,17,13,15};
    size_t first_last =0;
    size_t dec_pointer  = vec.size()-1;
    
    for(auto it =vec.cbegin(); it !=vec.cend() && !vec.empty();   it)
    {
        first_last = *it   *(it (dec_pointer--));
        std::cout<<"Add First and Last Digit : "<<first_last<<std::endl;
        dec_pointer--;
    }

    return 0;
}

CodePudding user response:

If you were adding the first and last elements (as per your text), you wouldn't need a loop, you could just do (after checking minimum size, of course):

first_plus_last = *(vec.cbegin())   *(vec.cend()-1);

It looks however that you're trying to add the first and last, second and second last, and so on. The reason why you would have to decrement twice is because you're getting the second iterator value by adding something to the current iterator value (not the start iterator).

For example, let's for the purposes of understanding just pretend they're indexes rather than iterators:

index:  0  1  2  3  4  5  6  7  8
value:  1  4  2  6  9 10 17 13 15

To correctly add the first (moving) index and a delta value to get the second index, you would need:

index1  index2  index2 as (index1   delta)
------  ------  --------------------------
     0       8            0   8
     1       7            1   6
     2       6            2   4
... and so on

You can see that the required delta is decreasing by two each time: 8, 6, 4, ....


But, rather than doing iterator calculations, I would opt for just running an iterator from both ends, toward the middle. In other words, something like this:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec{1, 4, 2, 6, 9, 10, 17, 13, 15};

    if (vec.size() > 0) {
        auto left = vec.cbegin();
        auto right = vec.cend() - 1;
        while (left < right) {
            auto num1 = *left  ;
            auto num2 = *right--;
            auto sum = num1   num2;
            std::cout << "Add (" << num1 << ", " << num2 << "): " << sum << '\n';
        }
        if (left == right) {
            std::cout << "One number left in middle: " << *left << '\n';
        }
    }
}

That seems like cleaner (as in "easier to understand") code to me, and the output is:

Add (1, 15): 16
Add (4, 13): 17
Add (2, 17): 19
Add (6, 10): 16
One number left in middle: 9

It also works with all the possible vector sizes (empty, one element, even number of elements, and odd number of elements greater than one).

CodePudding user response:

If you used vec.cbegin() instead of it when calculating the last pointer, you would not have this issue.

first_last = *it   *(vec.cbegin()   dec_pointer--)

CodePudding user response:

On the 1st loop iteration, it refers to the first element, and dec_pointer is 8, so you have:

-----------------------------------------
| 1 | 4 | 2 | 6 | 9 | 10 | 17 | 13 | 15 |
-----------------------------------------
  ^                                  ^ 
  it                                it 8

If you then increment it by 1 and decrement dec_pointer by 1 not 2, on the 2nd iteration dec_pointer is now 7 and you have this:

-----------------------------------------
| 1 | 4 | 2 | 6 | 9 | 10 | 17 | 13 | 15 |
-----------------------------------------
      ^                              ^ 
      it                            it 7

If you then increment it by 1 and decrement dec_pointer by 1 not 2, on the 3rd iteration dec_pointer is now 6 and you have this:

-----------------------------------------
| 1 | 4 | 2 | 6 | 9 | 10 | 17 | 13 | 15 |
-----------------------------------------
          ^                          ^ 
          it                        it 6

And so on. So, as you can see, the last value you refer to with it dec_pointer is always the same element 15. That is why you have to decrement dec_pointer by 2 instead of 1 to get the correct result:

-----------------------------------------
| 1 | 4 | 2 | 6 | 9 | 10 | 17 | 13 | 15 |
-----------------------------------------
  ^   ^   ^                ^    ^    ^ 
  it  |   |                |    |   it 8
      |   |                |    |
     it 1 |                | (it 1) 6
          |                |
         it 2           (it 2) 4

That being said, I would suggest an alternative and simpler solution - use a reverse_iterator from vec.crbegin() to access the 2nd value, instead of offsetting a forward iterator from vec.cbegin(), eg:

#include <iostream>
#include <vector>
#include <string>

int main()
{
    std::vector<int> vec{1,4,2,6,9,10,17,13,15};
    auto last_it = vec.crbegin();
    
    for(auto it = vec.cbegin(); it != vec.cend();   it,   last_it)
    {
        int first_last = *it   *last_it;
        std::cout << "Add First and Last Digit : " << first_last << std::endl;
    }

    return 0;
}

Online Demo

Or even simpler, just get rid of the it loop iterator completely:

#include <iostream>
#include <vector>
#include <string>

int main()
{
    std::vector<int> vec{1,4,2,6,9,10,17,13,15};
    auto last_it = vec.crbegin();

    for(int first : vec)
    {
        int first_last = first   *last_it  ;
        std::cout << "Add First and Last Digit : " << first_last << std::endl;
    }

    return 0;
}

Online Demo

  • Related