3

I just started learning C++ by myself using Lippman, Lajoie & Moo's C++ Primer Fifth Edition (5th printing, may 2014) in september 2014. Some exercises in that book I could do, some I had to look here for help and at this one I'm stuck for days. I searched Google, blogs and other forums and got nothing, so I ask you for help. It is the exercise 3.24 at page 113, which asks the same as the exercise 3.20 in page 105, but using iterators:

Read a set of integers into a vector. Print the sum of each pair of adjacent elements. Change your program so that it prints the sum of the first and last elements, followed by the sum of the second and second-to-last, and so on.

Using iterators as it asked, I could do the first part:

#include <iostream>
#include <vector>

using std::cin; using std::cout; using std::endl; using std::vector;

int main()
{
   vector<int> lista;
   int num_entra = 0;

   while (cin >> num_entra)
       lista.push_back(num_entra);


   cout << "Sum of adjacent pairs: " << endl;
   for (auto it = lista.begin(); it != lista.end() - 1; ++it)
   {
       *it += *(it + 1);
       cout << *it << ' ';
   }

   return 0;
}

The code above works as intended, but the part where I need to sum the first and last, second and second to last... etc. has an issue I just can't solve:

int main()
{
   vector<int> lista;
   int num_entra = 0;

   while (cin >> num_entra)
       lista.push_back(num_entra);


   auto ult = lista.end();

   cout << "Sum of the first and last elements until the center: " << endl;
   for (auto it = lista.begin(); it != lista.end() - 1; ++it)
   {
       *it = *it + *(--ult);
       cout << *it << ' ';
   }

   return 0;
}

If the user enters 1 2 3 4 5 6, the program adds 1 + 6, 2 + 5 and 3 + 4 as it should,
but then it adds the last result (7) with 5 and then with 6 and I can't seem to find a way to stop this behavior. What can I do so the program shows only one sum for each pair until the center of the list? Thank you in advance.

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
Opa-Opa
  • 41
  • 4
  • What's the point listing a bunch of `using`s, you may as well just use the whole namespace and be done with it. – Neil Kirk Nov 05 '14 at 21:16
  • I'm just following the style of C++ Primer. I just copy and paste the header in new files. Is this not safer than using the whole namespace? – Opa-Opa Nov 05 '14 at 21:20
  • Note that the code you've written for the first part of the exercise won't work if the vector is empty – Khalida Aliyeva Jul 04 '20 at 09:45

5 Answers5

2

One way to do this would be to use two iterators

auto front = lista.begin();  // start at front and increment
auto back = lista.end() - 1; // start at back and decrement
for (;
     back > front;     // stop when the iterators cross each other
     ++front, --back)
{
    int total = *front + *back;
    std::cout << total << ' ';
}
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • Thank you so much! It worked! I did not know you could declare the iterators outside the for loop and let an empty ";" inside it. – Opa-Opa Nov 05 '14 at 19:01
  • 1
    @Opa-Opa Im this particular case it is simply a bad idea to declare the iterators outside the loop. Moreover the code will not work if the vector is empty. – Vlad from Moscow Nov 05 '14 at 19:03
  • 1
    @VladfromMoscow In this case, it is trivial since they return from `main` immediately afterwards anyway ;) You are correct that some general error handling should be done to protect against empty lists, and they should decide what to do with odd-length lists (simply return the middle element? add middle to itself? something else?) – Cory Kramer Nov 05 '14 at 19:05
  • I'd suggest `while (back --> front++)`. It looks better imo. – edmz Nov 05 '14 at 19:07
  • @black I don't know what "-->" means. I'm in chapter 3 of C++ Primer and have not learned this feature yet. – Opa-Opa Nov 05 '14 at 19:35
  • @Opa-Opa `-->` is not a function as you think it is. The spacing is misleading. It actually means `back--` and `>` and `front++`. The `--` is the decrement operator (opposite of `++`) and the `>` is just greater than. – Cory Kramer Nov 05 '14 at 19:36
  • @Opa-Opa It's aka as "going to" operator. Further info [here](http://stackoverflow.com/questions/1642028/what-is-the-name-of-the-operator). – edmz Nov 05 '14 at 20:02
0

My five cents. Only instead of the explicitly initialized vector you should enter values for it as you did.

#include <iostream>
#include <vector>

int main() 
{
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    if ( !v.empty() )
    {
        auto first = v.cbegin(), last = v.cend();
        do
        {
            std::cout << *first + *--last;
        } while ( first++ != last );
    }

    return 0;
}

The output is

666

This approach you also can use for example with std::list

If you need to skip the middle odd element then the general approach for containers that have no random access iterator is the following

#include <iostream>
#include <vector>

int main() 
{
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    for ( auto first = v.cbegin(), last = v.cend(); 
          first != last && first != --last;
          ++first )
    {
        std::cout << *first + *last;
    }

    std::cout << std::endl;

    return 0;
}

As vectors have random acces iterators then the code can be rewritten using operator <

#include <iostream>
#include <vector>

int main() 
{
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    if ( !v.empty() )
    {
        for ( auto first = v.cbegin(), last = v.cend(); 
              first < --last;
              ++first )
        {
            std::cout << *first + *last;
        }

        std::cout << std::endl;
    }
    return 0;
}       

In both cases the output will be

66
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you! It was like this that I wanted to make the code, but how do I make the odd element to be ignored? Also, I have not learned std::list yet. – Opa-Opa Nov 05 '14 at 20:43
0

The problem with your code is that you're modifying the vector as you compute the sums by dereferencing the iterator (might not be a problem but it's a likely unwanted side effect) and you're not even stopping the cycle when you've "surpassed" the middle of the vector.

A simple version could be

auto front = lista.begin();
auto back = lista.end() - 1;
for (; back > front; ++front, --back)
{
    int total = *front + *back;
    std::cout << total << ' ';
}

but this doesn't handle an odd number of elements like 1 2 3 4 5 6 7 (the central 4 isn't printed). If you don't want any "same element" couple, this is fine and this solution will handle every case you need, otherwise go ahead.

A fixed version could be

auto front = lista.begin();
auto back = lista.end() - 1;
for (; back >= front; ++front, --back)
{
    if (front == back)
        cout << *front;
    else {
        int total = *front + *back;
        std::cout << total << ' ';
    }
}

but this doesn't handle a null list [].

This solution handles both although it's more complex:

auto it = lista.begin();
    auto ult = lista.rbegin();
    size_t dist;
    for ( ; it != lista.end() && // I have a valid front iterator
            ult != lista.rend() && // I have a valid back iterator
            (dist = std::distance(it,ult.base()), dist) > 0; // Their distance is greater than 0 (1 is included)
    ++it,++ult ) {
        if (dist == 1)
            cout << *it;
        else
            cout << *it + *ult << ' ';
    }
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • I prefer to not print the odd number element, but thank you for the explanation! I'm taking notes. The solution to handle a null list is a bit too far from my understanding right now. – Opa-Opa Nov 05 '14 at 20:34
  • @Opa-Opa not a problem, just be aware of the issue and perhaps come back to this code in the future, it will be way easier to understand – Marco A. Nov 05 '14 at 20:34
0

In addition to incrementing one iterator while decrementing another, I think the best way is to use a reverse iterator.

auto it = list.begin();
auto rit = list.rbegin(); // a "reverse iterator"
while (it < rit.base()) {
  cout << *it++ + *rit++ << ' ';
}

Using a reverse iterator means you don't have to worry about any "off-by-one" adjustments due to the asymmetry between begin and end.

Josh
  • 992
  • 5
  • 5
  • Thank you! I did not learn reverse iterators yet, good to know there is this option too! I used your code and noticed it does an extra sum when the pairs are odd. Entering "1 2 3 4 5" gives "6 6 6" instead of just "6 6". Why is that? – Opa-Opa Nov 05 '14 at 19:46
  • If there's an odd number of elements, it adds the middle one to itself. E.g. 1+5, 2+4, and 3+3. That seemed sensible to me... otherwise should it just print 3? Or nothing? – Josh Nov 05 '14 at 20:07
  • Oh, I got it. Thanks. I prefer to just print nothing in case of odd elements. – Opa-Opa Nov 05 '14 at 20:31
0

Here is also one approach where mid is an iterator closest to the middle of the array, which is also commonly used in binary search implementation:

  auto e = lista.end() - 1;
  auto mid = lista.end() + (lista.begin() - lista.end()) / 2;
  for (auto i = lista.begin(); i != mid; ++i, --e){
    cout << *i << "+" << *e << " = " << (*i)+(*e) << endl;
  }

Input: 1 2 3 4 5 6 7 8 9;

Positions/index: 0 1 2 3 4 5 6 7 8 9

Where mid = (0 - 10) / 2 => -5 + 10 => 5 The loop stops after reading number five.

And at index 5 is number 6. Therefore the loop stops when it hits the iterator closest to the middle:

i != mid;

 /*Output*/
 1+9 = 10
 2+8 = 10
 3+7 = 10
 4+6 = 10
 5+5 = 10

PS. I recommend you do checks to see if the vector is empty.

Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • The book taught this "mid" approach, but when I tried to use this method for the exercise I could not wrap my head around it and got a lot of compile errors, so I gave up and tried other ways. Nice to know how to make it work! It needs two iterators. Thank you! Also, if I don't want to sum in case of odd elements, I just put "i != mid - 1". Great! – Opa-Opa Nov 05 '14 at 20:29