简体   繁体   中英

How to better understand nested loops?

My problem is that I don't understand nested loops well enough to answer this problem. I'm supposed to right-align a stack that I've made on a left alignment using nested for loops, but I can't quite figure out the conditions on the two inner ones.

Correct answer:

Height = 8
.......#
......##
.....###
....####
...#####
..######
.#######
########

My answer:

Height = 8
.......#
.......#......#
.......#......#.....#
.......#......#.....#....#
.......#......#.....#....#...#
.......#......#.....#....#...#..#
.......#......#.....#....#...#..#.#
.......#......#.....#....#...#..#.##

I've played around with it, took it seriously and nothing. I did (k = 7, k > j, k--), (k = 0, k < n-1, k++), k < j+7, I drew tables and i know that the height is pretty much the same as the value of the spaces but inverted on each line. I also know that the value of the hashes and the spaces should be equal to the height input by user.

It's supposed to take in a value from user, but I've worked on it on a separate file with the value n being the height to simplify and work on it without the rest of the program.

#include <stdio.h>

int main(void) {
    int n = 8;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < i; j++) {
            for(int k = 7; k > j; k--) {
                printf(".");
            }
            printf("#");
        }
        printf("\n");
    }
}

It's actually pretty simple. Write a table with each line and how many spaces and '#' you need to print:

n == 8

| output   | line | num_spaces | num_signs |
| -------- | ---- | ---------- | --------- |
| .......# | 1    | 7          | 1         |
| ......## | 2    | 6          | 2         |
| .....### | 3    | 5          | 3         |
| ....#### | 4    | 4          | 4         |
| ...##### | 5    | 3          | 5         |
| ..###### | 6    | 2          | 6         |
| .####### | 7    | 1          | 7         |
| ######## | 8    | 0          | 8         |    

For line you can start from 0 or from 1 or from n and go backwards. Pick something that is the easiest. You will see that starting from 1 is the simplest in your example.

Now for each line we need to determine how many num_spaces and num_signs we print. They should depend on line and on n .

For num_spaces it's n - line and for num_signs it's line

So the code should look like this:

// for each line
for (int line = 1; line <= n; ++line)
{
    // print n - line spaces

    // print line # characters

    // print \n
}

With loops the code will look like this:

// for each line
for (int line = 1; line <= n; ++line)
{
    // print n - line spaces
    for (int i = 0; i < n -line; ++i)
        std::cout << ' ';

    // print line # characters
    for (int i = 0; i < line; ++i)
        std::cout << '#';

    std::cout << '\n';
}

std::cout.flush();

But that's actually not recommended. You can get rid of those inner loops. One good and easy way is to use strings:

// for each line
for (int line = 1; line <= n; ++line)
{
    // print n - line spaces
    std::cout << std::string(n - line, ' ');

    // print line # characters
    std::cout << std::string(line, '#');

    std::cout << '\n';
}

std::cout.flush();

And you can go even one step further:

// for each line
for (int line = 1; line <= n; ++line)
{
    // print n - line spaces and line # characters
    std::cout << std::string(n - line, ' ') << std::string(line, '#') << '\n';
}

std::cout.flush();

I'm supposed to right-align a stack that I've made on a left alignment using nested for loops, but I can't quite figure out the conditions on the two inner ones.

I think your word problem can be made more solvable by using somewhat more meaningful variable and function names. The words might help guide how you think about your effort, and certainly reveal more to your readers. Single char symbols have little meaning.

On the other hand, there do exist several efforts where single char names and vars are more than tolerated, but actually expected ... for example, matrix math (perhaps for computer generated graphics) often use indexes named simply x,y,z and perhaps t, and would cause no issues for the careful author.


I have prepared 4 examples for you, which I have labeled 'X', 'a', 'b', and 'c'. Each example is packaged as a functor (for my convenience).

To begin, functors 'X' and 'a' are the "same", such that I would expect the compiler to gen very similar code. You might not be able to confirm that with a single glance. The difference is that Example 'X' has single char vars and function names. And example 'a' has more than 1 letter symbols, but not full 'properly spelled' words, either.

Functor Descriptions:

  1. 'X' iterative, returns int 0, cout of step pattern

    • vars: n, i, j, k

    • functions: x(), p()

  2. 'a' iterative, returns int 0, cout of step pattern

    • vars: maxPtrnHgtWdth, step

    • function: exec(), dotPndLine()

goal: Perhaps these longer symbol names might help guide development effort, or at least make it easier reading.


class T934X_t  // 'X'
{
public:
   int operator()(int64_t n) { return x(n); }

private:
   int x(int64_t n)
      {
         cout << "\n\n\n  int T934X_t()(int64_t) -->"
              << " int x (int64_t)     (" << n
              << ")\n      iterative, return 0, cout step pattern, vars: n, i, j, k    ";

         for (uint i = 0; i < n; ++i) {
            p(n, i+1);
         }
         return 0;
      }
   //                     1..n
   void p (int64_t n, uint j)
      {
         cout << "\n  ";
         for (uint k = 0; k < (n - j); ++k) { cout << '.'; }
         for (uint k = 0; k < j;       ++k) { cout << '#'; }
         cout << ' ' << flush;
      }
}; // class T934X_t



class T934a_t  // 'a'
{
public:
   int operator()(int64_t maxPtrnHgtWdth) { return exec(maxPtrnHgtWdth); }

private:
   int exec (int64_t maxPtrnHgtWdth)
      {
         cout << "\n\n\n  int T934a_t()(int64_t) -->"
              << " int exec (int64_t)     (" << maxPtrnHgtWdth
              << ")\n      iterative, return 0, cout 'step' pattern ";

         for (uint step = 0; step < maxPtrnHgtWdth; ++step) {
            dotPndLine (maxPtrnHgtWdth, step+1);
         }
         return 0; // meaningless
      }
   //                                            1..8
   void dotPndLine (int64_t maxPtrnHgtWdth, uint step)
      {
         cout << "\n  "; //                      8    - 1..8
         for (uint dotCt = 0; dotCt < (maxPtrnHgtWdth - step); ++dotCt) { cout << '.'; }
         for (uint pndCt = 0; pndCt < step;                    ++pndCt) { cout << '#'; }
         cout << " " << flush;
      }
}; // class T934a_t

More Functor Descriptions: (two more to consider)

  1. 'b': iterative, returns a string containing the step pattern

    • vars: maxPtrnHgtWdth, step
    • functions: exec(), dotPndLine()

Similar to 'a', but uses stringstream instead of 'on-the-fly-cout'. This is a simple kind of 'delayed' output technique. I use this to support the sharing (by multiple threads) of a single output stream (such as cout).

  1. 'c': tail recursive , return a string which contains step pattern

    • vars: maxPtrnHgtWdth, step, dotct, pndCt
    • functions: exec(), execR(), dotPndLine(), dotR(), pndR()

I have included because I am a fan of recursion, and thought these relatively simple examples of recursive looping might be helpful for someone new to recursion.


class T934b_t // 'b'
{
   stringstream ss;
   int64_t maxPtrnHgtWdth;

public:
   string operator()(int64_t a_max)
      {
         maxPtrnHgtWdth = a_max;
         exec ();
         return ss.str();
      }

private:
   void exec ()
      {
         ss << "\n\n\n  string T934b_t()(int64_t) -->"
            << " void exec (int64_t)     (" << maxPtrnHgtWdth
            << ")\n         iterative, return string which contains step pattern";

         for (int64_t step = 0; step < maxPtrnHgtWdth; ++step) // step 0..7
         {
            dotPndLine (step+1); // build each line with dots and pound signs
         }
      }
   //                       1..8
   void dotPndLine (int64_t step)
      {
         ss << "\n  ";  //                           8    - 1..8
         for (int64_t dotCt = 0;  dotCt < (maxPtrnHgtWdth - step); ++dotCt) { ss << '.'; } // dot
         for (int64_t pndCt = 0;  pndCt < step;                    ++pndCt) { ss << '#'; } // pnd
         ss <<  " ";
      }
}; // class T934b_t



class T934c_t // 'c'
{
   stringstream  ss;
   int64_t maxPtrnHgtWdth;

public:
   string operator()(int64_t max)
      {
         maxPtrnHgtWdth = max;
         exec();
         return ss.str();
      }

private:
   void exec ()
      {
         ss << "\n\n\n  string T934c_t()(int64_t): -->"
            << " void exec (int64_t)     (" << maxPtrnHgtWdth
            << ")\n         tail recursive, return string which contains step pattern, ";

         execR (maxPtrnHgtWdth); // entry to recursive code
      }
   //                  8..1
   void execR (int64_t step)
      {
         if (0 >= step) return;  // recursion termination
         dotPndLine (step-1);    // build each line with dots and pound signs
         execR (step-1);         // tail recursion
      }
   //                       7..0
   void dotPndLine (int64_t step)
      {
         ss << "\n  ";
         dotR (step);                 // 7..0  dots
         pndR (maxPtrnHgtWdth-step);  // 1..8  pound sign
         ss << ' ';
      }
   //                 7..0
   void dotR (int64_t dotCt) {
      if (0 >= dotCt) return;  // recursion termination
      ss << '.';
      dotR (dotCt-1);          // tail recursion
   }
   //                 1..8
   void pndR (int64_t pndCt) {
      if (0 >= pndCt)  return; // recursion termination
      ss << '#';
      pndR (pndCt-1);          // tail recursion
   }
}; // class T934c_t

I build these 4 functors (and main and some simple code to activate), all in a single file.

#include <iostream>
using std::cout, std::cerr, std::endl, std::flush;

#include <string>
using std::string, std::stol;

#include <sstream>
using std::stringstream;

#include <cassert>

class T934X_t  // 'X'
{
// ... move previous code here
}    

class T934a_t  // 'a' 
{
// ... move previous code here
}    

class T934b_t // 'b'
{
// ... move previous code here
}    

class T934c_t //  'c'
{
// ... move previous code here
}    

// main invokes the functor
class T934_t
{
public:
   int operator()(int argc, char* argv[])
      {
         if (argc < 2) {
            cerr << "\n  expecting height/width limit: uint > 2\n" << endl;
            return -1;
         }

         if(false) // enable for system info
         {
            string s;
            s.reserve(26000000);
            cout << "\n\n  T934_t()  (implementation details)"       // ubuntu, g++7
                 << "\n  sizeof(long int):  " << sizeof(long int)    //  8 bytes
                 << "\n  sizeof(int64_t) :  " << sizeof(int64_t)     //  8 bytes
                 << "\n  string.size()   :  " << s.size()            //  0 elements
                 << "         sizeof(string) : " << sizeof(string);     // 32 bytes

            for (int i=0; i<1000000; ++i)
               for (char kar='a'; kar<='z'; ++kar)
                  s += kar;

            cout << "\n  string.size()   :  " << s.size()           // 260000 elements
                 << "  sizeof(string) : "  << sizeof(string)        //     32 bytes
                 << endl;
         } // string s destructs here

         // user selected stair step max Pattern Height Width
         int64_t maxPtrnHgtWdth = stol(argv[1]);
         if (maxPtrnHgtWdth < 2)  { maxPtrnHgtWdth = 8; }
         else if (maxPtrnHgtWdth > 80) { maxPtrnHgtWdth = 79; } // arbitrary limit

         return exec (maxPtrnHgtWdth);
      }

private:

   // this functor exec() invokes 'X', 'a', 'b', and 'c'
   int exec (int64_t maxPtrnHgtWdth)
      {
         int retVal = 0;

         // iterative, return 0 ignored, cout step pattern, 
         //     vars: n, i, j, k, functions x(), p()
         (void)T934X_t()(maxPtrnHgtWdth);


         // iterative, return 0 ignored, cout 'step' pattern,
         //     functions exec(), dotPndLine()
         (void)T934a_t()(maxPtrnHgtWdth);


         // iterative, return string which contains step pattern
         cout << T934b_t()(maxPtrnHgtWdth) << flush;


         // tail recursive, returns string which contains step pattern
         cout << T934c_t()(maxPtrnHgtWdth) << flush;


         cout << "\n\n\n  T934_t::exec()"
              << "    (__cplusplus: " <<  __cplusplus  << ")" 
              << std::endl;
         return retVal;
      }
}; // class T934_t


int main(int argc, char* argv[]) { return T934_t()(argc, argv); }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM