这个C for-loop如何打印文本艺术金字塔?

Ale*_*l04 25 c for-loop

这是我第一次在这里发帖,希望我做得对.

基本上我需要帮助试图找出我用C编写的一些代码.程序的目的是要求用户输入0到23之间的数字.然后,根据用户输入的数字,半金字塔将打印(就像旧学校马里奥游戏中的那些).我是编程的初学者,仅凭运气就得到了我的代码的答案,但现在我无法确定我的for循环如何提供金字塔形象.

#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;
}
Run Code Online (Sandbox Code Playgroud)

作为编程的新手,我跟着我对伪代码的了解很少,我似乎无法理解为什么for循环部分按照它的方式工作.我完全理解while循环(虽然欢迎更正和最佳实践),但for循环逻辑仍然无法避开我,我想在继续之前完全理解它.任何帮助将不胜感激.

Bri*_*ths 58

我将解释我将如何理解这段代码的过程,以便我自己习惯使用它.我会假装我没有读你的描述,所以我从头开始.这个过程分为几个阶段,我将按照我的要求编号.我的目标是提供一些使程序更易于阅读的通用技巧.

第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);
Run Code Online (Sandbox Code Playgroud)

到目前为止,我们已经以整数声明,并告诉用户输入一个数字,然后使用该scanf函数设置等于用户输入的整数.不幸的是,从提示或代码中不清楚整数的目的是什么.让我们继续阅读.

    while (user_i < 0 || user_i > 23) {
        scanf("%d", &user_i);
    }
Run Code Online (Sandbox Code Playgroud)

在这里,我们可能会要求用户输入其他整数.根据提示判断,似乎是一个很好的猜测,这个语句的目的是确保我们的整数在适当的范围内,并且通过检查代码很容易检查.我们来看下一行

     for (int tall = 0; tall < user_i; tall++) {
Run Code Online (Sandbox Code Playgroud)

这是外部for循环.我们的神秘整数user_i再次出现,我们有另一个整数tall介于0和之间user_i.让我们看看更多代码.

        for (int space = 0; space <= user_i - tall; space++) {
            printf(" ");
        }
Run Code Online (Sandbox Code Playgroud)

这是第一个内循环.让我们不要陷入这个新整数的细节space或我们user_i - tall出现的原因,但是让我们注意,foor循环的主体只是打印一个空格.所以这个for循环只是打印一堆空格的效果.让我们看看下一个内部for循环.

        for (int hash = 0; hash <= tall; hash++) {
            printf("#");
        }
Run Code Online (Sandbox Code Playgroud)

这个看起来很相似.它只打印了一堆哈希.接下来我们有

        printf("\n");
Run Code Online (Sandbox Code Playgroud)

这会打印一个新行.接下来是

    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这意味着外部for循环结束,并且在外部for循环结束之后,程序结束.

请注意,我们已经找到了代码的两个主要部分.第一个部分是user_i获取值的位置,第二个部分是外部for循环,必须是此值用于绘制金字塔的位置.接下来让我们试着弄明白是什么user_i意思.

第二阶段:发现的意义 user_i

现在,因为为外循环的每次迭代打印了一个新行,并且神秘user_i控制了外循环的迭代次数,因此打印了多少个新行,它似乎user_i控制了金字塔的高度.创建.为了获得精确的关系,让我们假设user_i有值3,然后tall取值0,1和2,这样循环将执行三次,金字塔的高度将为三.另请注意,如果user_i增加1,则循环将再次执行,金字塔将增加1.这意味着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;
}
Run Code Online (Sandbox Code Playgroud)

第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;
}
Run Code Online (Sandbox Code Playgroud)

第4阶段:理解内部for循环.

我们知道内部for循环重复打印一个字符,但是多少次?让我们考虑第一个内部for循环.打印了多少个空格?您可能认为通过类比外部for循环有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;
}
Run Code Online (Sandbox Code Playgroud)

现在,当我查看该main函数时,我不必担心printSpaces实际打印空间的细节.我已经忘记了它是否使用for循环或while循环.这让我的大脑自由地思考其他事情.

第6阶段:引入变量并为变量选择好名称

我们的main功能现在很容易阅读.我们准备开始考虑它实际上做了什么.for循环的每次迭代都会打印一定数量的空格,然后是一定数量的散列,后跟一个新行.由于首先打印空格,所以它们都在左边,这就是我们想要得到的图片.

由于新行打印在终端上的旧行下方,因此零值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;
}
Run Code Online (Sandbox Code Playgroud)

第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,使得程序的关键逻辑很容易看到.

  • +为你的努力解释. (6认同)

Bil*_*eal 12

首先,让我们把循环体取出来.第一个只打印空格,第二个打印哈希标记.

我们想要打印这样的一行,其中_是一个空格:

______######
Run Code Online (Sandbox Code Playgroud)

所以神奇的问题是,我们需要打印多少个空格和#s?

在每一行,我们希望打印比之前的行多1个#,并且比之前的行少1个空格.这是"高"的目的在外循环中起作用.您可以将其视为"应在此行上打印的哈希标记数".

要在线上打印的所有剩余字符应为空格.因此,我们可以获取总行长度(用户输入的数字),并减去此行上的散列标记数量,这就是我们需要的空格数量.这是第一个for循环中的条件:

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

然后我们需要打印哈希标记的数量,它总是等于当前行:

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

这一切都在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;
}
Run Code Online (Sandbox Code Playgroud)


Jam*_*son 8

一些东西:

  • 考虑"基准检查"这个.也就是说,自己跟踪循环并绘制空格和哈希标记.考虑使用方格纸.我已经这样做了15年了,当他们毛茸茸的时候,我仍然不时在纸上追踪.

  • 其中的神奇之处在于值"user_i - tall"和"hash <= tall".这两个内部for循环的条件是括号中的中间值.注意他们在做什么:

  • 因为tall从最外层循环"向上",通过从user_i中减去它,打印空间的循环"向下".也就是说,随着时间的推移打印的空间越来越少.

  • 因为高是"上升",并且因为哈希循环基本上是按原样使用它,所以它也在上升.也就是说,随你打印更多的哈希标记.

所以真的忽略了大部分代码.它是通用的:外部循环只是计数,大多数内部循环只进行基本初始化(即space = 0和hash = 0),或基本增量(space ++和hash ++).

它只是内环的中心部分,它们使用来自最外环的高移动,分别向上和向上递增,如上所述,从而使空间和散列标记的组合成为一半-金字塔.希望这可以帮助!


jed*_*rds 7

这是你的代码,只是重新格式化并删除了评论:

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");   
}
Run Code Online (Sandbox Code Playgroud)

并且相同的代码,插入了一些解释:

// 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");   
}
Run Code Online (Sandbox Code Playgroud)