Подтвердить что ты не робот

Как этот C-for-loop печатает текстовые пирамиды?

Это мой первый пост, здесь, надеюсь, я делаю это правильно.

В основном мне нужна помощь, пытаясь выяснить какой-то код, который я написал для класса с использованием C. Цель программы - запросить у пользователя число от 0 до 23. Затем, исходя из числа введенного пользователем, будет напечатана половина пирамиды (например, в старой школе Mario games). Я новичок в программировании и получил ответ на мой код, просто удачи, но теперь я не могу сказать, как мои for-loops обеспечивают цифру пирамиды.

#include <stdio.h>

int main ( void )
{
    int user_i;
    printf ( "Hello there and welcome to the pyramid creator program\n" );
    printf ( "Please enter a non negative INTEGER from 0 to 23\n" );
    scanf ( "%d", &user_i );

    while ( user_i < 0 || user_i > 23 )
    {
        scanf ( "%d", &user_i );
    }

    for ( int tall = 0; tall < user_i; tall++ )
    {
        // this are the two for loops that happened by pure magic, I am still
        // trying to figure out why are they working they way they are
        for ( int space = 0; space <= user_i - tall; space++ )
        {
            printf ( " " );
        }
        for ( int hash = 0; hash <= tall; hash++ )
        {
            printf ( "#" );
        }
        // We need to specify the printf("\n"); statement here
        printf ( "\n" );
    }

    return 0;
}

Будучи новичком в программировании, я следил за тем, что мало знаю о псевдокоде, я просто не могу понять, почему секция for-loop работает так, как она есть. Я прекрасно понимаю цикл while (хотя исправления и лучшие практики приветствуются), но логика for-loop продолжает ускользать от меня, и я хотел бы полностью понять это, прежде чем двигаться дальше. Любая помощь будет принята с благодарностью.

4b9b3361

Ответ 1

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

Этап 1: понимать грубую структуру

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

int main(void) {
    int user_i;
    printf("Hello there and welcome to the pyramid creator program\n");
    printf("Please enter a non negative INTEGER from 0 to 23\n");
    scanf("%d", &user_i);

До сих пор мы объявили в integer и сказали пользователю ввести число, а затем использовали функцию scanf, чтобы установить целое число, равное тому, что вводил пользователь. К сожалению, из подсказки или кода неясно, какова цель целого. Продолжайте читать.

    while (user_i < 0 || user_i > 23) {
        scanf("%d", &user_i);
    }

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

     for (int tall = 0; tall < user_i; tall++) {

Это внешний цикл. Наше загадочное целое число user_i появляется снова, и мы имеем другое целое число tall, которое идет между 0 и user_i. Давайте рассмотрим еще один код.

        for (int space = 0; space <= user_i - tall; space++) {
            printf(" ");
        }

Это первый внутренний цикл. Давайте не будем увязнуть в деталях того, что происходит с этим новым целым числом space или почему мы имеем user_i - tall, но давайте просто отметим, что тело петли foor просто печатает пробел. Таким образом, это для цикла просто приводит к печати пучка пространств. Давайте посмотрим на следующий внутренний цикл.

        for (int hash = 0; hash <= tall; hash++) {
            printf("#");
        }

Этот выглядит похожим. Он просто печатает кучу хэшей. Далее мы имеем

        printf("\n");

Это печатает новую строку. Далее

    }

    return 0;
}

Это означает, что конец внешнего цикла завершается, и после окончания внешнего цикла цикла программа заканчивается.

Заметьте, что мы нашли две основные части кода. Первая часть - это то, где получено значение user_i, а вторая часть, внешняя для цикла, должна быть там, где это значение используется для рисования пирамиды. Затем попробуйте выяснить, что означает user_i.

Этап 2: узнайте значение user_i

Теперь, когда новая строка печатается для каждой итерации внешнего цикла, а загадочный user_i определяет, сколько итераций внешнего цикла есть, и, следовательно, сколько новых строк напечатано, казалось бы, что user_i управляет высотой создаваемой пирамиды. Чтобы получить точное соотношение, предположим, что user_i имеет значение 3, тогда tall будет принимать значения 0,1 и 2, и поэтому цикл будет выполняться три раза, а высота пирамиды будет быть три. Также обратите внимание, что если user_i увеличилось бы на единицу, то цикл выполнил бы еще раз, а пирамида была бы выше на единицу. Это означает, что user_i должна быть высотой пирамиды. Переименуйте переменную в pyramidHeight, прежде чем мы забудем. Теперь наша основная функция выглядит следующим образом:

int main(void) {
    int pyramidHeight;
    printf("Hello there and welcome to the pyramid creator program\n");
    printf("Please enter a non negative INTEGER from 0 to 23\n");
    scanf("%d", &pyramidHeight);
    while (pyramidHeight < 0 || pyramidHeight > 23) {
        scanf("%d", &pyramidHeight);
    }

    for (int tall = 0; tall < pyramidHeight; tall++) {
        for (int space = 0; space <= pyramidHeight - tall; space++) {
            printf(" ");
        }
        for (int hash = 0; hash <= tall; hash++) {
            printf("#");
        }
        printf("\n");
    }

    return 0;
}

Этап 3: создайте функцию, которая получит высоту пирамиды

Поскольку мы понимаем первую часть кода, мы можем переместить его в функцию и больше не думать об этом. Это упростит просмотр кода. Поскольку эта часть кода отвечает за получение допустимой высоты, позвоните по телефону getValidHeight. После этого заметите, что высота пирамиды не изменится в методе main, поэтому мы можем объявить ее как const int. Теперь наш код выглядит следующим образом:

#include <stdio.h>

const int getValidHeight() {
    int pyramidHeight;
    printf("Hello there and welcome to the pyramid creator program\n");
    printf("Please enter a non negative INTEGER from 0 to 23\n");
    scanf("%d", &pyramidHeight);
    while (pyramidHeight < 0 || pyramidHeight > 23) {
        scanf("%d", &pyramidHeight);
    }
    return pyramidHeight;
}

int main(void) {
    const int pyramidHeight = getValidHeight();
    for (int tall = 0; tall < pyramidHeight; tall++) {
        for (int space = 0; space <= pyramidHeight - tall; space++) {
            printf(" ");
        }
        for (int hash = 0; hash <= tall; hash++) {
            printf("#");
        }
        printf("\n");
    }

    return 0;
}

Этап 4: понять внутренние циклы.

Мы знаем, что внутренняя для циклов печатает символ неоднократно, но сколько раз? Рассмотрим первый внутренний цикл. Сколько пробелов напечатано? Вы можете подумать, что по аналогии с внешним циклом существует pyramidHeight - tall пробелы, но здесь мы имеем space <= pyramidHeight - tall, где действительно аналогичная ситуация была бы space < pyramidHeight - tall. Поскольку мы имеем <= вместо <, мы получаем дополнительную итерацию, где space равно pyramidHeight - tall. Таким образом, мы видим, что на самом деле pyramidHeight - tall + 1 печатаются пробелы. Аналогично печатаются хеши tall + 1.

Этап 5: перемещение печати нескольких символов в их собственные функции.

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

#include <stdio.h>

const int getValidHeight() {
    int pyramidHeight;
    printf("Hello there and welcome to the pyramid creator program\n");
    printf("Please enter a non negative INTEGER from 0 to 23\n");
    scanf("%d", &pyramidHeight);
    while (pyramidHeight < 0 || pyramidHeight > 23) {
        scanf("%d", &pyramidHeight);
    }
    return pyramidHeight;
}

void printSpaces(const int numSpaces) {
    for (int i = 0; i < numSpaces; i++) {
        printf(" ");
    }
}

void printHashes(const int numHashes) {
    for (int i = 0; i < numHashes; i++) {
        printf("#");
    }
}

int main(void) {
    const int pyramidHeight = getValidHeight();
    for (int tall = 0; tall < pyramidHeight; tall++) {
        printSpaces(pyramidHeight - tall + 1);
        printHashes(tall + 1);
        printf("\n");
    }

    return 0;
}

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

Этап 6: введение переменных и выбор хороших имен для переменных

Теперь наша функция main легко читается. Мы готовы начать думать о том, что он на самом деле делает. Каждая итерация цикла for печатает определенное количество пробелов, за которым следует определенное количество хэшей, за которым следует новая строка. Поскольку сначала печатаются пробелы, все они будут слева, и это то, что мы хотим, чтобы получить изображение, которое оно дает нам.

Так как новые строки печатаются ниже старых строк на терминале, то значение 0 для tall соответствует верхней строке пирамиды.

С учетом этих данных, введем две новые переменные, numSpaces и numHashes для того, чтобы количество пробелов и хэшей было напечатано на итерации. Поскольку значение этих переменных не изменяется в одной итерации, мы можем сделать их константами. Также измените имя tall (которое является прилагательным и, следовательно, плохим именем для целого), на distanceFromTop. Наш новый главный метод выглядит следующим образом

int main(void) {
    const int pyramidHeight = getValidHeight();

    for (int distanceFromTop = 0; distanceFromTop < pyramidHeight; distanceFromTop++) {
        const int numSpaces = pyramidHeight - distanceFromTop + 1;
        const int numHashes = distanceFromTop + 1;
        printSpaces(numSpaces);
        printHashes(numHashes);
        printf("\n");
    }

    return 0;
}

Этап 7: Почему numSpaces и numHashes что они собой представляют?

Теперь все объединяется. Осталось только выяснить формулы, которые дают numSpaces и numHashes.

Давайте начнем с numHashes, потому что это легче понять. Мы хотим, чтобы numHashes был единым, когда расстояние от вершины равно нулю, и мы хотим, чтобы numHashes увеличивался на единицу всякий раз, когда расстояние от вершины делает, поэтому правильная формула numHashes = distanceFromTop + 1.

Теперь для numSpaces. Мы знаем, что каждый раз, когда расстояние от вершины увеличивается, пространство превращается в хеш, и поэтому есть еще одно пространство. Таким образом, выражение для numSpaces должно содержать -distanceFromTop. Но сколько мест должно иметь верхняя строка? Поскольку верхний ряд уже имеет хеш, есть pyramidHeight - 1 хэши, которые нужно сделать, поэтому должно быть не менее pyramidHeight - 1 пробелов, чтобы их можно было превратить в хэши. В коде мы выбрали pyramidHeight + 1 пробелы в верхней строке, что на два больше, чем pyramidHeight - 1, и поэтому имеет эффект перемещения всего изображения на два пробела.

Заключение

Вы только спрашивали, как работают две внутренние петли, но я дал очень длинный ответ. Это связано с тем, что я думал, что реальная проблема заключается не в том, что вы не понимали, как работают петли достаточно хорошо, а скорее из-за того, что ваш код трудно читать, и поэтому было трудно сказать, что что-то делает. Поэтому я показал вам, как бы я написал программу, надеясь, что вы подумаете, что ее легче читать, поэтому вы сможете увидеть, что происходит более четко, и, надеюсь, вы сможете научиться писать более четкий код самостоятельно.

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

Ответ 2

Во-первых, позвольте получить тело петель. Первая только печатает пробелы, а вторая печатает отметки хэша.

Мы хотим напечатать такую ​​строку, где _ - пробел:

______######

Итак, волшебный вопрос: сколько пробелов и # нужно нам печатать?

В каждой строке мы хотим напечатать еще 1 #, чем раньше, и 1 меньше пробелов, чем строка раньше. Это цель, "высокая" служит во внешнем цикле. Вы можете думать об этом как о "числе хэш-меток, которые должны быть напечатаны на этой строке".

Все остальные символы для печати на линии должны быть пробелами. В результате мы можем взять общую длину строки (который является номером, введенным пользователем), и вычесть количество хэш-меток в этой строке и количество требуемых пробелов. Это условие в первом цикле:

for ( int space = 0; space <= user_i - tall; space++ )
//                            ~~~~~~~~~~~~~ 
// Number of spaces == number_user_entered - current_line

Затем нам нужно напечатать количество хэш-меток, которое всегда равно текущей строке:

for ( int hash = 0; hash <= tall; hash++ )
//                          ~~~~
// Remember, "tall" is the current line

Вся эта вещь находится в цикле for, который просто повторяется один раз в строке.

Переименование некоторых переменных и введение некоторых новых имен может сделать все это намного проще:

#include <stdio.h>

int main ( void )
{
    int userProvidedNumber;
    printf ( "Hello there and welcome to the pyramid creator program\n" );
    printf ( "Please enter a non negative INTEGER from 0 to 23\n" );
    scanf ( "%d", &userProvidedNumber );

    while ( userProvidedNumber < 0 || userProvidedNumber > 23 )
    {
        scanf ( "%d", &userProvidedNumber );
    }

    for ( int currentLine = 0; currentLine < userProvidedNumber; currentLine++ )
    {
        int numberOfSpacesToPrint = userProvidedNumber - currentLine;
        int numberOfHashesToPrint = currentLine;

        for ( int space = 0; space <= numberOfSpacesToPrint; space++ )
        {
            printf ( " " );
        }
        for ( int hash = 0; hash <= numberOfHashesToPrint; hash++ )
        {
            printf ( "#" );
        }

        // We need to specify the printf("\n"); statement here
        printf ( "\n" );
    }

    return 0;
}

Ответ 3

Несколько вещей:

  • Подумайте об этом. То есть, проведите через петли самостоятельно и вычеркните пробелы и метки хэша. Подумайте об использовании графической бумаги. Я делаю это в течение 15 лет, и я все время прослеживаю вещи на бумаге время от времени, когда они волосатые.

  • Магия в этом значении "user_i - tall" и "hash <= tall". Это условия для двух внутренних циклов в качестве средних значений в скобках. Обратите внимание, что они делают:

  • Поскольку высокий "поднимается" из внешнего цикла, вычитая его из user_i, цикл, который печатает пробелы, "опускается". То есть, печать меньше и меньше пробелов.

  • Потому что высокий "поднимается", а потому, что хеш-цикл просто использует его как есть, он тоже растет. То есть, печатайте больше символов хэша, когда идете.

Так что действительно игнорируйте большую часть этого кода. Он общий: внешний цикл только подсчитывается, и большинство внутренних циклов просто выполняют базовую инициализацию (т.е. Space = 0 и hash = 0) или базовое инкрементное (space ++ и hash ++).

Это только центральные части внутренних петель, которые имеют значение, и они используют движение высоких от самой внешней петли, чтобы увеличивать себя вниз и вверх соответственно, как отмечено выше, таким образом, делая комбинацию пробелов и хэш-меток, необходимых для сделайте полупирамиду. Надеюсь, это поможет!

Ответ 4

Вот ваш код, просто переформатированный и лишенный комментариев:

for(int tall = 0;tall<user_i;tall++)
{
    for(int space=0; space<=user_i-tall; space++){ 
        printf(" "); 
    }
    for(int hash=0; hash<=tall; hash++){ 
        printf("#");
    }
    printf("\n");   
}

И тот же код с некоторым объяснением:

// This is a simple loop, it will loop user_i times.
//   tall will be [0,1,...,(user_i - 1)] inclusive.
//   If we peek at the end of the loop, we see that we're printing a newline character.
//   In fact, each iteration of this loop will be a single line.
for(int tall=0; tall<user_i; tall++)
{
    // "For each line, we do the following:"
    //
    // This will loop (user_i - tall + 1) times, each time printing a single space.
    //   As we've seen, tall starts at 0 and increases by 1 per line.
    //   On the first line, tall = 0 and this will loop (user_i + 1) times, printing that many spaces
    //   On the last line, tall = (user_i - 1) and this will loop 0 times, not printing any spaces
    for(int space=0; space<=user_i-tall; space++){ 
        printf(" "); 
    }

    // This will loop (tall + 1)  times, each printing a single hash
    //   On the first line, tall = 0 and this will loop 1 time, printing 1 hash
    //   On the last line, tall = (user_i - 1) and this will loop user_i times, printing that many hashes
    for(int hash=0; hash<=tall; hash++){ 
        printf("#");
    }

    // Finally, we print a newline
    printf("\n");   
}

Ответ 5

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

  • n нет строк для печати в пирамиде n =5
  • Замена пустых пространств ' ' на '-' (символ тире)

Теперь пирамида будет выглядеть примерно так:

                            ----#
                            ---##
                            --###
                            -####
                            #####

Теперь шаги по созданию цикла,

  • Прежде всего, мы должны напечатать строки n, т.е. 5 строк, поэтому первый цикл будет выполняться 5 раз.
    for (int rowNo = 0; rowNo < n; rowNo++ ) Число строк rowNo похоже на tall в вашем цикле
  • В каждой строке мы должны печатать символы 5, но мы получаем желаемую цифру, если мы внимательно рассмотрим, что такое логика,
    rowNo=0 (Это наша первая строка), мы имеем 4 тире и 1 хэш rowNo=1 мы имеем 3 тире и 2 хэш rowNo=2 мы имеем 2 тире и 3 хэш rowNo=3 мы имеем 1 тире и 4 хэш rowNo=4 мы имеем 0 тире и 5 хеш
  • Небольшая проверка может показать, что для каждой строки, обозначенной rowNo, нам нужно напечатать n - rowNo - 1 тире - и rowNo + 1 хэши #,
    Поэтому внутри нашего первого цикла for мы должны иметь две петли, одну для печати тире и одну для печати хэшей.
    Цикл тире будет for (int dashes= 0; dashes < n - rowNo - 1; dashes ++ ), здесь dashes похож на space в вашей исходной программе
    Контур хэш будет for (int hash = 0; hash < rowNo + 1; dashes ++ ),
  • Последний шаг после каждой строки мы должны напечатать разрыв строки, чтобы мы могли перейти к следующей строке.

Надеемся, что приведенное выше объяснение дает четкое представление о том, как будут создаваться петли for, которые вы написали.

В вашей программе есть только незначительные изменения, тогда мое объяснение, в моих циклах я использовал меньше, чем оператор <, и вы использовали оператор <=, чтобы он отличался от одной итерации.

Ответ 6

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

Что делает ваш код, это печатать строки user_i, состоящие из пробелов user_i-tall+1, а затем хэшей tall+1. Поскольку индекс итерации tall увеличивается с каждым проходом, это означает, что печатается еще один хеш. Они выровнены по правому краю, так как пространство также отсутствует. Впоследствии у вас есть "растущие" ряды хешей, которые образуют пирамиду.
То, что вы делаете, эквивалентно этому псевдокоду:

for every i between 0 and user_i do:
    print " " (user_i-i+1) times
    print "#" (i+1) times

Ответ 7

Анализ ваших циклов:
Первый цикл

for ( int tall = 0; tall < user_i; tall++ ){...}

управляет строкой. Второй цикл

for ( int space = 0; space <= user_i - tall; space++ ){...}  

для столбца, заполняемого пробелами.
Для каждой строки он заполняет все столбцы user_i - tall пробелами.
Теперь остальные столбцы заполняются # контуром

for ( int hash = 0; hash <= tall; hash++ ){...}  

Ответ 8

#include <stdio.h>

// Please note spacing of
// - functions braces
// - for loops braces
// - equations
// - indentation
int main(void)
{
    // Char holds all the values we want
    // Also, declaire all your variables at the top
    unsigned char user_i;
    unsigned char tall, space, hash;

    // One call to printf is more efficient
    printf("Hello there and welcome to the pyramid creator program\n"
           "Please enter a non negative INTEGER from 0 to 23\n");

    // This is suited for a do-while. Exercise to the reader for adding in a
    // print when user input is invalid.
    do scanf("%d", &user_i);
    while (user_i < 0 || user_i > 23);

    // For each level of the pyramid (starting from the top)...
    // Goes from 0 to user_i - 1
    for (tall = 0; tall < user_i; tall++) {

        // We are going to make each line user_i + 2 characters wide

        // At tall = 0,          this will be user_i + 1                characters worth of spaces
        // At tall = 1,          this will be user_i + 1            - 1 characters worth of spaces
        // ...
        // At tall = user_i - 1, this will be user_i + 1 - (user_i - 1) characters worth of spaces
        for (space = 0; space <= user_i - tall; space++)
            printf(" "); // no '\n', so characters print right next to one another

        //                 because of using '<=' inequality
        //                                                \_  
        // At tall = 0,          this will be          0 + 1 characters worth of hashes
        // At tall = 1,          this will be          1 + 1 characters worth of hashes
        // ...
        // At tall = user_i - 1, this will be user_i - 1 + 1 characters worth of spaces
        for (hash = 0; hash <= tall; hash++)
            printf("#");

        // Level complete. Add a newline to start the next level
        printf("\n");   
    }

    return 0;
}

Ответ 9

Самый простой ответ на вопрос, почему это происходит, состоит в том, что один цикл печатает пробелы, такие как: -

----------
---------
--------
-------
------
-----

и т.д. Хеши печатаются следующим образом

------#
-----##
----###
---####
--#####
-######

так как цикл печати хэш на втором этапе должен печатать равное количество хэшей больше для завершения пирамиды, он может быть решен двумя способами: одним путем копирования и вставки хэш-цикла дважды или путем повторного цикла цикла в следующий раз, следуя модификации

for ( int hash = 0; hash <= tall*2; hash++ )
        {
            printf ( "#" );
        }

Логика построения таких циклов проста: самый внешний контур печатает один канал для разделения петли, внутренние контуры отвечают за каждое содержимое строк. Цикл пространства помещает пробелы и хеш-петлю, добавляя хэш в конце пробелов. [Мой ответ может быть лишним, потому что я не читал другие ответы так тщательно, они были длинными] Результат:

       #
      ###
     #####
    #######
   #########
  ###########

Ответ 10

for ( int tall = 0; tall < user_i; tall++ ) { ... }

Я думаю об этом на естественном языке:

  • int tall = 0;//Начнем с начального значения tall = 0
  • tall < user_i;//В то время как высокий показатель меньше user_i, делайте материал между фигурными фигурными скобками
  • высокий ++;//В конце каждого цикла увеличиваем высоту, а затем повторно проверяем условие на шаге 2 перед выполнением другого цикла

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