Some brief comments:
Your variable names are quite short (often one letter), which makes the code harder to follow. Longer and more descriptive variable names would make it easier to read and debug (e.g., trg
to triangle
, r1
to row1
).
The input to the function printTrg
is rows
, but we also have variables like r
, r1
and r2
which look like rows. Perhaps this would be better named as row_count
?
The main style guide for Python is PEP 8, which is very readable and friendly. It gives advice on how you choose variable names, functions, and so on. In particular, for function names, it says:
Function names should be lowercase, with words separated by underscores as necessary to improve readability.
So you should rename the function printTrg
to be print_trg
, or even better print_triangle
, to match the Python conventions.
There's also something called a docstring, which is used in Python functions to explain what the function is supposed to do.
This usually occurs in triple quotes, directly below the def
line.
For example, you might write:
def printTrg(rows):
"""Prints Pascal's triangle up to the (rows)th row."""
...
There's a more detailed explanation of docstrings in PEP 257.
Right now the code which generates Pascal's triangle, and the code which prints it to the console, are tightly bound together.
You might be better off splitting the two into two functions:
generate_pascals_triangle(row_count)
, which would return the list of lists trg
which your function already creates.
print_pascals_triangle(triangle)
, which could take the output from generate
and print it to the console.
The elif rows == 2
block is redundant, as the else
block does exactly the same thing. The range(2, rows)
never executes if rows
is 2.
While we're in the else
block, I’m unclear why your range goes to rows
in the first for loop, but to len(trg)
in the second. Unless I've confused myself, these are always the same thing. And later in the same loop, you use rows
to count the number of spaces you need.
The way you generate the next row of Pascal's triangle (insert a list of 1s, update the entries, then put an extra 1 on the end) could be simplified. Since you’re making a list for the entries in that row, you could use a list comprehension. (If you're not familiar with these, then there are lots of good explanations on Google.)
Here's a simple example of a function which takes the previous row of Pascal's triangle, works out which row you're in now (by looking at the length of the previous row), then generates the new row with a list comprehension:
def next_row(row):
"""Returns the next row of Pascal's triangle."""
n = len(row)
new_row = [row[0]] + [row[i] + row[i+1] for i in range(n - 1)] + [row[-1]]
return new_row
We're still doing essentially the same thing, but this looks cleaner, and we've split up some more of the logic. This can make life easier when it comes to debugging.
This also works if we give it the first row [1]
, so we don't need to consider the cases rows == 1
and rows != 1
separately when we generate Pascal's triangle. Again, we're able to simplify some of the code.
As you’ve pointed out, things get out of line if you print a large number of rows, as two and three digit numbers come in to play. If you split this into two functions, then you can play around with the printing without affecting the arithmetic (which works perfectly).
So with all that in mind, here's how I might rewrite your code.
First I have the function which I mentioned above, which takes one row of Pascal's triangle and returns the next:
def next_row(row):
"""Returns the next row of Pascal's triangle."""
n = len(row)
new_row = [row[0]] + [row[i] + row[i+1] for i in range(n - 1)] + [row[-1]]
return new_row
Next, we have a function which just generates the entries of Pascal's triangle. Since we're not printing anything (that will come later), and the arithmetic for generating new rows is in another function, this is much shorter than before:
def generate_pascals_triangle(row_count):
"""Returns the entries of Pascal's triangle."""
row1 = [1]
triangle = [row1]
for i in range(1, row_count):
triangle.append(next_row(triangle[-1]))
return triangle
If we wanted, we could supply the first entry (or even a whole first row) as an argument. You could see what happens if you make 2 the top entry, or started several levels deep. This is left for you to play with. (You may find this explanation of optional and named arguments useful.)
Finally, we come to the printing problem. We can use the logic from your else
block, almost unmodified (just with more descriptive variable names):
def print_pascals_triangle(triangle):
"""Prints the entries of Pascal's triangle."""
for row_no in range(len(triangle)):
row = triangle[row_no]
printed_row = []
for entry in row:
printed_row.append(str(entry))
print (' ' * (len(triangle) - (row_no + 1)), ' '.join(printed_row))
If you wanted, that could become a little shorter using list comprehensions (I leave that as an exercise).
Here's an idea for how you could solve the spacing issue: first, find the longest number in your triangle. (You don't need to examine every entry: which row will always contain the longest entry?) Then, add spaces to every number you print, so that they're always the same length.
For example, if the longest entry was 2704156
(which you get in a 25-triangle), you would transform
1
-> _ _ _ 1 _ _ _
15
-> _ _ 1 5 _ _ _
165
-> _ _ 1 6 5 _ _
and so on. I haven't tried that, so I don't know how good it looks, but it might be something for you to get you started.
ETA: One more comment, based on your updated code:
At the end of the script, you have a little “interactive” section, which gets the user to give you a row count, and then you print their Pascal’s triangle.
If you load this file with import pascal-triangle
in a larger script, then you immediately get asked to start drawing Pascal triangles. This can be unhelpful (imagine if everything you import
ed did that!).
Instead, you can put this code inside a special if
statement:
if __name__ == '__main__':
again = 'y'
while again == 'y':
rows = int(input('Enter the number of rows you want in your Pascal`s Triangle: '))
print_pascals_triangle(generate_pascals_triangle(rows))
again = input('Do it again ? <y/n> ')
Any code within this if
statement is only used if the script is run directly; that is, if somebody types
$ python pascal-triangle.py
at a command prompt. If it gets loaded as import pascal-triangle
in another script, then any code inside this if
statement won’t be run. This means that you can reuse these functions later without any hassle.
You can find out more in this Stack Overflow question: https://stackoverflow.com/questions/419163/what-does-if-name-main-do