简体   繁体   中英

C - Printing a specific pattern

The exercise asks me to create a program which receives an integer number n and prints the following pattern with a 2*n height.

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

What I've already done is:

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

This only prints the backslashes on the top and I don't know how to continue the code. I would like to know if it's a good way to do it and what is the better way to continue the code.

I'd develop the solution in stages, roughly like this. My solution insists on the n parameter (for which I use N in the code) is an odd number. The question doesn't show how to present the result if it is even. The insistence is backed up by assertions.

Stage 1 — print line function

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

The repeat_char() function is a very simple little loop that prints the specified character the specified number of times. By making it static inline , there's a good chance that the compiler will not make a function call but will place the body of function into the calling code.

The print_line() function characterizes each line as:

  • zero or more leading blanks
  • one occurrence of c1
  • zero or more inner blanks (that must be an even number)
  • nc occurrences of c2 (that must be an odd number)
  • zero or more inner blanks again
  • one occurrence of c3
  • in other circumstances, it might be useful to add trailing blanks; that's commented out
  • in those other circumstances, the function wouldn't print a newline

The code in main() calls that function with appropriate arguments written out manually. It generates the output:

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

This looks like what you're after for N=5. But those argument lists to the the function have an awful lot of regularity to them. There must be a way to generate the calls with those numbers replaced by expressions. This observation leads to stage 2.

Stage 2 — two loops

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

The repeat_char() and print_line() functions are unchanged from before. The new function, driver_1() , contains two loops, one to process the rows with 0, 1, … N-1 leading blanks, and the other to process the rows with N-1, N-2, … 0 leading blanks. The min() and max() functions are again static inline so that their use is unlikely to incur function call overhead. The loop index, i , controls the number of leading blanks. The expressions for nb and db calculate how many blanks and asterisks to output. Those expressions are the same in both loops; the differences are in the direction of counting (up vs down) and the order of the slash character arguments.

This generates the output:

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

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

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

This demonstrates that the functions work with different sizes of output requested.

Stage 3 — single loop

The final version of the code uses the symmetries of the two loops in driver_1() and uses a single loop over the range 0 .. 2* N - 1 to generate the correct call to print_line() . The only changes are in the driver function, renamed to driver_2() (in part because it was developed in a single executable which also had driver_1() in it). Again, the repeat_char() and print_line() functions are unchanged; and min() and max() are reused too.

The loop determines the value corresponding to i in driver_1() using the expression (strictly, that's a definition, not an expression, but it contains an expression) int i = min(j, 2*N-1-j); . The j term counts up; the 2*N-1-j term counts down; the value used is the smaller of those two. The test for j == i allows the correct choice of first and last character.

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

This generates the output (note the almost degenerate case for N=1):

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

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

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

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

\*/
/*\

So, there you have it. A complete development cycle in 3 stages. Get something working and demonstrate that it works. Then make the solution more general.

There are many ways to achieve this. One of them is the following

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--;
    }
}

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