4
\$\begingroup\$

I am beginner in C++ and I was given this assignment by my teacher. The assignment is to print the following shape:

      *
     * *
    *   *
   *     *
  *       *
 *         *
*************

The following is what I have tried and basically it works, but I doubt my code is not simple, there's another way simpler than that. How can I make my code more simpler? Any hints are appreciated.

#include <iostream>

using namespace std;

int main() {
  /**
      *
     * *
    *   *
   *     *
  *       *
 *         *
*************
  */
  int spaces_before = 6; // spaces before printing the stars
  int spaces_after = 0; // spaces after printing the first star
  int stars = 1;
  for (int i = 1; i <= 7; i++) { 
    for (int j = 1; j <= spaces_before; j++) {
      cout << ' ';
    }
    if (i == 1) {
      cout << "*";
    } else if (i != 7) {
      cout << '*';
      for (int j = 1; j <= spaces_after; j++) {
        cout << ' ';
      }
      cout << '*';
    } else {
      for (int j = 1; j <= stars; j++) {
        cout << '*';
      }
    }
    spaces_before--;
    if (i == 1) {
      spaces_after++;
    } else {
      spaces_after += 2;
    }
    cout << '\n';
    stars += 2; 
  }
}
\$\endgroup\$
3
\$\begingroup\$

First of all, please avoid using namespace std;. It is considered bad practice and will cause many problems. See Why is using namespace std; considered bad practice? for more information.

Now let's try to simplify the logic. The following loop:

for (int j = 1; j <= spaces_before; j++) {
  cout << ' ';
}

is a very verbose way to write

std::cout << std::string(spaces_before, ' ');

(this happened more than one time.)

And you don't need to keep the variables spaces_before, spaces_after, and star. Just calculate them on demand. Also, don't include the special cases in the loop:

#include <iostream>
#include <string>

int main()
{
    std::cout << "      *\n";
    for (int i = 0; i < 5; ++i)
        std::cout << std::string(5 - i, ' ') << '*'
                  << std::string(2 * i + 1, ' ') << "*\n";
    std::cout << std::string(13, '*') << '\n';
}

This should be enough since you are a beginner. After this is done, it would be very nice if you extract the magic numbers into named constants. And the whole process can be wrapped in a function for reuse:

#include <iostream>
#include <string>

void print(std::ostream& os, int rows)
{
    os << std::string(rows - 1, ' ') << "*\n";
    for (int i = 1; i < rows - 1; ++i)
        os << std::string(rows - i - 1, ' ') << '*'
                  << std::string(2 * i - 1, ' ') << "*\n";
    os << std::string(2 * rows - 1, '*') << '\n';
}

int main()
{
    print(std::cout, 7);
}
\$\endgroup\$
2
\$\begingroup\$

Rather than creating strings (as @L.F. has done) I'd consider using the stream's setw manipulator to place the asterisks as needed (at least for all but the last row).

So, given a width, we can then define a center. For what I'm going to call row 0, the first asterisk gets printed at the center: cout << std::setw(center) << "*\n";.

For rows 1 through N-1, we print two asterisks, one at center-i, and the other at center+i. Unfortunately, those are both relative to the left margin, but what we need for std::setw is relative to the current position on the line, after the previous write. To do that, we have to compute the distance from the left asterisk to the right asterisk. Fortunately, that's pretty simple: one space, then three spaces, then 5 spaces, and so on up to the number of rows we decide to print.

For the final line, we have a number of asterisks followed by a new-line. We certainly could use setw and company to do this as well:

std::cout << std::setfill('*') << std::setw(2*rows) << '\n';

...or we could use a std::string:

std::cout << std::string(2*rows-1, '*') + "\n";

I don't see huge advantages or disadvantages to either method though.

For the moment I'll skip over discussing putting the code into a function-- @L.F. has already covered that fairly well, with a couple of exceptions. First, I don't like the name print, which conveys little about what it really does. Second, I'm not entirely excited about combining generating the triangle with actually printing the results to a stream. I'd personally prefer to keep those to aspects separate.

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>

std::string triangle(int rows) { 
    std::stringstream buf;
    buf << std::setw(rows+1) << "*\n";

    for (int i=1; i<rows-1; i++) {
        buf << std::setw(rows-i) << "*" << std::setw(2*i+1) << "*\n";
    }
    buf << std::setfill('*') << std::setw(2*rows) << '\n';
    return buf.str();
}

int main() { 
    const int rows = 7;

    std::cout << triangle(rows);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.