C - 打印特定图案

时间:2017-05-07 16:10:22

标签: c

练习要求我创建一个程序,该程序接收整数n并打印以下pattern高度为2*n的<{p}}。

\    *    /
 \  ***  /
  \*****/
   \***/
    \*/
    /*\
   /***\
  /*****\
 /  ***  \
/    *    \

我已经做的是:

void arte(int n)
{
    int i, barrasE, aux, espacobarrasE, ebE, espaco;
    aux = n;

    for(i = 1; i <= n; i++)
    {
       if(aux < n) //Prints the spaces on the superior part.
       {
           espacobarrasE = n - aux;
           for(ebE = 0; ebE < espacobarrasE; ebE++)
               printf(" ");
       }
       for(barrasE = 1; barrasE <= aux; barrasE++) //Prints the backslashes on the superior part.
       {
           printf("\\");
           break;
       }
       for(espaco = 1; espaco < n; espaco++)
       {
           printf(" ");
       }
       aux = aux - 1;
       printf("\n");
   }
}

这只打印顶部的反斜杠,我不知道如何继续代码。我想知道这是否是一个很好的方法,以及继续代码的更好方法。

2 个答案:

答案 0 :(得分:3)

我会分阶段开发解决方案,大致是这样的。我的解决方案坚持n参数(我在代码中使用N)是一个奇数。问题没有说明如果结果是偶数的话如何呈现结果。断言支持坚持。

第1阶段 - 打印行功能

#include <assert.h>
#include <stdio.h>

enum { FSLASH = '/', BSLASH = '\\' };

static inline void repeat_char(int num, char c) { for (int i = 0; i < num; i++) putchar(c); }

static void print_line(int l_blanks, char c1, int i_blanks, char c2, int nc, char c3)
{
    assert(i_blanks % 2 == 0);
    assert(nc % 2 == 1);
    repeat_char(l_blanks, ' ');
    putchar(c1);
    repeat_char(i_blanks, ' ');
    repeat_char(nc, c2);
    repeat_char(i_blanks, ' ');
    putchar(c3);
    //repeat_char(l_blanks, ' ');
    putchar('\n');
}

int main(void)
{
    print_line(0, BSLASH, 4, '*', 1, FSLASH);
    print_line(1, BSLASH, 2, '*', 3, FSLASH);
    print_line(2, BSLASH, 0, '*', 5, FSLASH);
    print_line(3, BSLASH, 0, '*', 3, FSLASH);
    print_line(4, BSLASH, 0, '*', 1, FSLASH);
    print_line(4, FSLASH, 0, '*', 1, BSLASH);
    print_line(3, FSLASH, 0, '*', 3, BSLASH);
    print_line(2, FSLASH, 0, '*', 5, BSLASH);
    print_line(1, FSLASH, 2, '*', 3, BSLASH);
    print_line(0, FSLASH, 4, '*', 1, BSLASH);
    putchar('\n');

    return 0;
}

repeat_char()函数是一个非常简单的小循环,它以指定的次数打印指定的字符。通过使它成为static inline,编译器很可能不会进行函数调用,而是将函数体放入调用代码中。

print_line()函数将每一行描述为:

  • 零个或多个前导空白
  • 出现一次c1
  • 零个或多个内部空白(必须是偶数)
  • nc出现c2(必须是奇数)
  • 再次出现零个或多个内部空白
  • 出现一次c3
  • 在其他情况下,添加尾随空白可能会有用;那个已经注释掉了
  • 在其他情况下,该功能不会打印换行符

main()中的代码调用该函数,并使用手动写出的适当参数。它生成输出:

\    *    /
 \  ***  /
  \*****/
   \***/
    \*/
    /*\
   /***\
  /*****\
 /  ***  \
/    *    \

这看起来像你在N = 5时所追求的。但是这个函数的参数列表对它们来说有很多规律性。必须有一种方法来生成调用,并将这些数字替换为表达式。这一观察结果导致第2阶段。

第2阶段 - 两个循环

#include <assert.h>
#include <stdio.h>

enum { FSLASH = '/', BSLASH = '\\' };

static inline void repeat_char(int num, char c) { for (int i = 0; i < num; i++) putchar(c); }

static inline int max(int x, int y) { return (x > y) ? x : y; }
static inline int min(int x, int y) { return (x < y) ? x : y; }

static void print_line(int l_blanks, char c1, int i_blanks, char c2, int nc, char c3)
{
    assert(i_blanks % 2 == 0);
    assert(nc % 2 == 1);
    repeat_char(l_blanks, ' ');
    putchar(c1);
    repeat_char(i_blanks, ' ');
    repeat_char(nc, c2);
    repeat_char(i_blanks, ' ');
    putchar(c3);
    //repeat_char(l_blanks, ' ');
    putchar('\n');
}

static void driver_1(int N)
{
    assert(N % 2 == 1 && N > 0);

    for (int i = 0; i < N; i++)
    {
        int nb = max(0, (N-1-2*i)/2);
        int db = min(2*i+1, 2*(N-i)-1);
        print_line(i, BSLASH, 2*nb, '*', db, FSLASH);
    }

    for (int i = N-1; i >= 0; i--)
    {
        int nb = max(0, (N-1-2*i)/2);
        int db = min(2*i+1, 2*(N-i)-1);
        print_line(i, FSLASH, 2*nb, '*', db, BSLASH);
    }
    putchar('\n');
}

int main(void)
{
    int N = 5;
    assert(N % 2 == 1);

    driver_1(N);
    driver_1(N+2);
    driver_1(N-2);

    return 0;
}

repeat_char()print_line()功能与以前相同。新函数driver_1()包含两个循环,一个用于处理具有0,1,... N-1个前导空白的行,另一个用于处理具有N-1,N-2,... 0行的行空白。 min()max()函数再次为static inline,因此它们的使用不太可能导致函数调用开销。循环索引i控制前导空格的数量。 nbdb的表达式计算输出的空白和星号数量。这两个循环中的表达式是相同的;差异在于计数(向上与向下)和斜线字符参数的顺序。

这会生成输出:

\    *    /
 \  ***  /
  \*****/
   \***/
    \*/
    /*\
   /***\
  /*****\
 /  ***  \
/    *    \

\      *      /
 \    ***    /
  \  *****  /
   \*******/
    \*****/
     \***/
      \*/
      /*\
     /***\
    /*****\
   /*******\
  /  *****  \
 /    ***    \
/      *      \

\  *  /
 \***/
  \*/
  /*\
 /***\
/  *  \

这表明这些函数适用于所请求的不同输出大小。

第3阶段 - 单循环

代码的最终版本使用driver_1()中两个循环的对称性,并使用范围0 .. 2* N - 1上的单个循环来生成对{{print_line()的正确调用1}}。唯一的变化是在驱动程序函数中,重命名为driver_2()(部分原因是它是在单个可执行文件中开发的,其中也包含driver_1())。同样,repeat_char()print_line()函数不变;并且min()max()也可以重复使用。

循环使用表达式确定idriver_1()对应的值(严格来说,这是一个定义,而不是表达式,但它包含一个表达式)int i = min(j, 2*N-1-j);j一词有效; 2*N-1-j一词倒计时;使用的值是这两者中较小的一个。 j == i的测试允许正确选择第一个和最后一个字符。

#include <assert.h>
#include <stdio.h>

enum { FSLASH = '/', BSLASH = '\\' };

static inline void repeat_char(int num, char c) { for (int i = 0; i < num; i++) putchar(c); }

static inline int max(int x, int y) { return (x > y) ? x : y; }
static inline int min(int x, int y) { return (x < y) ? x : y; }

static void print_line(int l_blanks, char c1, int i_blanks, char c2, int nc, char c3)
{
    assert(i_blanks % 2 == 0);
    assert(nc % 2 == 1);
    repeat_char(l_blanks, ' ');
    putchar(c1);
    repeat_char(i_blanks, ' ');
    repeat_char(nc, c2);
    repeat_char(i_blanks, ' ');
    putchar(c3);
    //repeat_char(l_blanks, ' ');
    putchar('\n');
}

static void driver_2(int N)
{
    assert(N % 2 == 1 && N > 0);
    for (int j = 0; j < 2*N; j++)
    {
        int i = min(j, 2*N-1-j);
        int nb = max(0, (N-1-2*i)/2);
        int db = min(2*i+1, 2*(N-i)-1);
        char c1 = (j == i) ? BSLASH : FSLASH;
        char c3 = (j == i) ? FSLASH : BSLASH;
        print_line(i, c1, 2*nb, '*', db, c3);
    }
    putchar('\n');
}

int main(void)
{

    int N = 5;
    assert(N % 2 == 1);

    driver_2(N);
    driver_2(N+2);
    driver_2(N+4);
    driver_2(N-2);
    driver_2(N-4);

    return 0;
}

这会产生输出(注意N = 1的几乎退化的情况):

\    *    /
 \  ***  /
  \*****/
   \***/
    \*/
    /*\
   /***\
  /*****\
 /  ***  \
/    *    \

\      *      /
 \    ***    /
  \  *****  /
   \*******/
    \*****/
     \***/
      \*/
      /*\
     /***\
    /*****\
   /*******\
  /  *****  \
 /    ***    \
/      *      \

\        *        /
 \      ***      /
  \    *****    /
   \  *******  /
    \*********/
     \*******/
      \*****/
       \***/
        \*/
        /*\
       /***\
      /*****\
     /*******\
    /*********\
   /  *******  \
  /    *****    \
 /      ***      \
/        *        \

\  *  /
 \***/
  \*/
  /*\
 /***\
/  *  \

\*/
/*\

所以,你有它。完整的开发周期分为3个阶段。得到一些有用的东西并证明它有效。然后使解决方案更加通用。

答案 1 :(得分:2)

有很多方法可以实现这一目标。 其中一个是以下

void print_pattern(int n) {

    int padding=0;
    int width=0;
    char char_begin='\\', char_end='/';

    assert(n>0);
    for(int x=0; x<n*2; x++) {
        if(padding==n) {
            padding--;
            char_begin='/';
            char_end='\\';
        }
        width=(n*2)-padding;
        for(int y=0;y<=width; y++) {
            if(y==padding)
                printf("%c",char_begin);
            else
                if (y==width)
                    printf("%c",char_end);
                else 
                    if (y>padding && y>=n-padding && y<=n+padding)
                        printf("*");
                    else
                        printf(" ");
        }
        printf("\n");
        if(x<n)
            padding++;
        else
            padding--;
    }
}