Как лучше понять вложенные циклы? - PullRequest
2 голосов
/ 27 апреля 2019

Моя проблема в том, что я недостаточно хорошо понимаю вложенные циклы, чтобы ответить на эту проблему.Я должен выровнять стек по правому краю с помощью вложенных циклов for, но я не могу точно определить условия для двух внутренних.

Правильный ответ:

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

Мой ответ:

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

Я играл с этим, воспринял это всерьез и ничего.Я сделал (k = 7, k> j, k--), (k = 0, k

Предполагается, что оно будет принимать значение от пользователя, но я работал над ним в отдельном файле со значением n, представляющим собой высоту для упрощения и работы с ним без остальной части программы.

#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");
    }
}

Ответы [ 2 ]

4 голосов
/ 06 мая 2019

Это на самом деле довольно просто.Напишите таблицу с каждой строкой и сколько пробелов и '#' вам нужно напечатать:

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         |    

Для line вы можете начать с 0 или 1 или n иидти назад.Выберите что-нибудь самое простое.Вы увидите, что начиная с 1 - это самый простой пример в вашем примере.

Теперь для каждого line нам нужно определить, сколько num_spaces и num_signs мы печатаем.Они должны зависеть от line и n.

Для num_spaces это n - line и для num_signs это line

Таким образом, код должен выглядеть следующим образом:

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

    // print line # characters

    // print \n
}

С циклами код будет выглядеть так:

// 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();

Но на самом деле это не рекомендуется.Вы можете избавиться от этих внутренних петель.Один хороший и простой способ - использовать строки:

// 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();

И вы можете пойти еще дальше:

// 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();
0 голосов
/ 06 мая 2019

Я должен выровнять стек по правому краю по левому используя вложенные для циклов, но я не могу понять, условия на два внутренних.

Я думаю, что ваша проблема со словом может быть сделана более разрешимой, если использовать более значимые имена переменных и функций. Эти слова могут помочь вам понять, как вы думаете о своих усилиях, и, безусловно, откроют больше для ваших читателей. Символы одиночного символа не имеют большого значения.

С другой стороны, существует несколько попыток, когда отдельные имена символов и переменные более чем допустимы, но фактически ожидаемы ... например, матричная математика (возможно, для компьютерной графики) часто использует индексы, называемые просто x, y , z и, возможно, t, и не вызовет проблем у внимательного автора.


Я подготовил для вас 4 примера, которые я обозначил «X», «a», «b» и «c». Каждый пример упакован как функтор (для моего удобства).

Для начала, функторы 'X' и 'a' являются «одинаковыми», так что я ожидаю, что компилятор генерирует очень похожий код. Вы не сможете подтвердить это одним взглядом. Разница в том, что в примере 'X' есть одиночные переменные и имена функций. И пример «а» имеет более 1 буквенных символов, но не полные «правильно написанные» слова, либо.

Описание функтора:

  1. 'X' итеративный, возвращает int 0, шаблон шага

    • переменные: n, i, j, k

    • функции: x (), p ()

  2. 'a' итеративный, возвращает int 0, шаблон cout of step

    • переменные: maxPtrnHgtWdth, шаг

    • функция: exec (), dotPndLine ()

цель: Возможно, более длинные имена символов могут помочь в разработке или, по крайней мере, облегчат чтение.


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

Больше описаний функторов: (еще два для рассмотрения)

  1. 'b': итеративный, возвращает строку, содержащую шаблон шага

    • переменные: maxPtrnHgtWdth, шаг
    • функции: exec (), dotPndLine ()

Похож на 'a', но использует stringstream вместо 'on-the-fly-cout'. Это простой вид «отложенного» метода вывода. Я использую это для поддержки совместного использования (несколькими потоками) одного выходного потока (например, cout).

  1. 'c': хвостовая рекурсия , возвращает строку, которая содержит шаблон шага

    • vars: maxPtrnHgtWdth, step, dotct, pndCt
    • функции: exec (), execR (), dotPndLine (), dotR (), pndR ()

Я включил, потому что я фанат рекурсии, и подумал, что эти относительно простые примеры рекурсивного зацикливания могут быть полезны для новичка в рекурсии.


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

Я строю эти 4 функтора (и основной, и некоторый простой код для активации), все в одном файле.

#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); }
...