8
\$\begingroup\$

I am new to Python and only just started learning about functions. I stumbled upon a question which recommends using function to print triangles in such a format:

Enter height: 5

         /\
        /  \
       /    \
      /      \
     /________\
    /\        /\
   /  \      /  \
  /    \    /    \
 /      \  /      \
/________\/________\

Enter height: 2

   /\
  /__\
 /\  /\
/__\/__\

Although I have gone through and successfully coded for the requirements. I feel though there needs to be a better approach. I was wondering if someone could review my code and point me in the right direction as to how I might improve on it.

This is what I currently have:

import sys

def triangle(height):
    print()
    max = (height * 2) - 1
    mid = 0
    while max > height:
        statement = " " * max + "/" + " " * mid + "\\"
        print(statement)
        max -= 1
        mid += 2
    statement = " " * max + "/" + "_" * mid + "\\"
    max -= 1
    print(statement)
    small = 0
    while max > 0:
        statement = " " * max + "/" + " " * small + "\\" + " " * mid + "/" + " " * small + "\\"
        print(statement)
        mid -= 2
        max -= 1
        small += 2
    statement = " " * max + "/" + "_" * small + "\\" + " " * mid + "/" + "_" * small + "\\"
    print(statement)
    pass

if __name__ == "__main__":
    userInput = input("Enter height: ")
    try:
        userInput = int(userInput)
    except ValueError:
        sys.exit("\nInvalid height.")
    if userInput < 2 or userInput > 20:
        sys.exit("\nInvalid height.")
    triangle(userInput)

I am curious as to there is a way to only code for one triangle and print multiple.

\$\endgroup\$
2
\$\begingroup\$

Your code seems to be working fine, but I have some suggestions:

  • Make sure that no line is longer than 80 characters
  • Instead of exiting when input is <2 or >20, instead prompt for a new input (your code also works for input 1, but not for 0)
  • Instead of the function handling the printing make it return the statement string
  • Avoid having variables with the names of builtins, e.g. max
  • The last pass doesn't add any functionality

To further improve your solution, I would do one of two things:

Continue with the same logic, but reduce the cluttering

I would definitely change from using while loops to using for loops. With a bit of math, the function can be reduced to:

def create_triangle(n):
    triangle = ""
    for i in range(n):
        triangle += ' '*(2*n-i-1) + '/' + ' _'[i==n-1]*2*i + '\\' + "\n"
    for i in range(n, 2*n):
        triangle += ' '*(2*n-i-1) + '/' + ' _'[i==2*n-1]*(2*i-2*n) + '\\'
        triangle += ' '*(4*n-2*i-2) + '/' + ' _'[i==2*n-1]*(2*i-2*n) + '\\'
        triangle += '\n'
    return triangle

Most of it is similar to what you have in your code, the only new thing being ' _'[i==n-1]*2*i. This statement could be hard to interpret, but the only thing it does is to print _ when i==n-1, and a space otherwise.

Change to a more general approach for these kinds of problems

If you want to solve more complex problems related to creating patterns with strings, I'd instead use an array based solution. A function like this:

def create_print_array(n):
    arr = [['*' for i in range(n)] for j in range(n)]
    return '\n'.join([''.join(row) for row in arr])

returns a pattern like this:

*****
*****
*****
*****
*****

for input n = 5. Since we have a 2d matrix, we can directly manipulate the values to form our desired output. With a bit of math again, the function can be written as:

def create_triangle_array(n):
    # create a 2D array filled with spaces
    arr = [[' ' for i in range(4*n)] for j in range(2*n)]
    # create the outer triangle legs
    for i in range(2*n):
        arr[i][2*n-i-1] = '/'
        arr[i][2*n+i] = '\\'
    # create the inner triangle legs
    for i in range(n, 2*n):
        arr[i][i] = '\\'
        arr[i][4*n-i-1] = '/'
    # create the bases for all three triangles
    for i in range(2*n-2):
        arr[n-1][n+1+i] = '_'
        arr[2*n-1][2*n+1+i] = '_'
        arr[2*n-1][1+i] = '_'
    # join the array into a string
    return '\n'.join([''.join(row) for row in arr])

Both of these approaches give the same form on the output, but it should be noted that the second approach gives some extra spaces on the end of each line, to make each line equally long. Both approaches also work for input >= 0 (I left out error-handling for input < 0 since you already covered that in your code).

As a final note, I'd say that your code looks very good for a beginner. It is easy to follow, and you have covered potential wrong input from the user.

\$\endgroup\$
  • \$\begingroup\$ Thank you. I am having trouble understanding how ' _'[i==2*n-1]*(2*i-2*n) works. I sort of get what it does but not sure how it achieves it. As I am new, I still haven't gotten used to being able to read the short form of the code. Also, for your suggestion Make sure that no line is longer than 80 character I have seen people using ENTER to break the line up but when I do that, my code seems to break. \$\endgroup\$ – Nauman Shahid Apr 19 '18 at 22:31
  • \$\begingroup\$ I debated with myself whether to include that syntax or not. Basically it works in two steps. Since ' _'[i] prints the i:th character of the string, you can also use ' _'[False/True] to print the first or second character of the string. Then the last part simply multiplies that character so that it's printed the correct number of times. When it comes to breaking lines, Python is not as intuitive as other languages. I'd suggest splitting long calculations into multiple steps, like I do in the last for-loop in the first example. You can also add a backslash to the end of a line to continue. \$\endgroup\$ – maxb Apr 20 '18 at 7:20
2
\$\begingroup\$

Not an answer, but an extended comment.

One approach, which I cannot recommend for this particular assignment, is to prepare a grid filled with spaces, slashes, backslashes, and underscores, and print it as a grid. Separating business logic from the output is usually a way to go.

To prepare the grid, observe that the figure you print consists of 3 triangles:

         /\
        /  \
       /    \
      /      \
     /________\

,

    /\        
   /  \      
  /    \    
 /      \  
/________\

and

             /\        
            /  \      
           /    \    
          /      \  
         /________\

which are identical, and differ only in their respective position. This make them ideal candidates for a function

    def draw_triangle(grid, top_x, top_y, height):
        ....

which would be called as

    draw_triangle(grid, 0, size/2, height)
    draw_triangle(grid, size, size/4, height)
    draw_triangle(grid, size, 3*size/4, height)

Another candidate for decomposing the problem is a function to draw a horizontal span of a figure. This would make a perfect sense when you'd need to draw this pyramid of n triangles high.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.