0. Delete all the comments
These paragraph-length comments make the code very hard to read. Comments should be a last resort for understanding the code since programmers only read them when they can't figure out what the code is doing. Well-written code doesn't just make the computer do the correct thing, but is also easy to understand by humans. Let's get rid of the comments and simplify the code.
The fewer words written, the fewer chances for mistakes.
1. Variable names
Picking accurate and specific variable names makes reasoning about the code easier. Let's look at basic_pascals()
:
def basic_pascals(num):
history_variable = [[1], [1, 1]]
save_variable = [1, 1]
current_variable = []
amount = 0
if num == 0:
return([1])
elif num == 1:
return([1, 1])
for i in range(num-1):
for item in zip(save_variable, save_variable[1:]):
amount += sum(item)
current_variable.append(amount)
amount = 0
current_variable.append(1)
current_variable.insert(0, 1)
save_variable = current_variable
current_variable = []
history_variable.append(save_variable)
return history_variable
The argument num
is the degree of the last polynomial in the triangle, so use degree
instead. The variable history_variable
is the Pascal's Triangle, so let's rename it triangle
. By tracking what happens to save_variable
through the function, we see that it is always equal to the current last row of history_variable
, so a natural name is last_row
. The variable current_variable
is the next row of the triangle being constructed, so let's call it next row
.
def basic_pascals(degree):
triangle = [[1], [1, 1]]
last_row = [1, 1]
next_row = []
amount = 0
if degree == 0:
return([1])
elif degree == 1:
return([1, 1])
for i in range(degree-1):
for item in zip(last_row, last_row[1:]):
amount += sum(item)
next_row.append(amount)
amount = 0
next_row.append(1)
next_row.insert(0, 1)
last_row = next_row
next_row = []
triangle.append(last_row)
return triangle
2. Create variables near where they are needed
Since you create the variables next_row
, last_row
, and amount
outside of the loops, you need to reset them at the end of the loops. If you move these to inside the loop, then they will be reset automatically at the beginning of each loop and you can delete the lines that reset the variables.
def basic_pascals(degree):
triangle = [[1], [1, 1]]
if degree == 0:
return([1])
elif degree == 1:
return([1, 1])
for i in range(degree-1):
next_row = []
last_row = triangle[-1]
for item in zip(last_row, last_row[1:]):
amount = 0
amount += sum(item)
next_row.append(amount)
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
Now we can see that amount
in the innermost loop is always equal to sum(item)
, so let's just use the latter expression.
def basic_pascals(degree):
triangle = [[1], [1, 1]]
if degree == 0:
return([1])
elif degree == 1:
return([1, 1])
for i in range(degree-1):
next_row = []
last_row = triangle[-1]
for item in zip(last_row, last_row[1:]):
next_row.append(sum(item))
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
3. Expressive loop conditions
How do we know we are done constructing the triangle? An n-th degree Pascal's Triangle has n+1 rows. This makes for a more expressive loop condition that lets the programmer know when the construction is complete. Most of the time, if a loop variable is not used, i
in this case, that is a good indication that there is a better way to write it.
def basic_pascals(degree):
triangle = [[1], [1, 1]]
if degree == 0:
return([1])
elif degree == 1:
return([1, 1])
while len(triangle) < degree + 1:
next_row = []
last_row = triangle[-1]
for item in zip(last_row, last_row[1:]):
next_row.append(sum(item))
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
4. Consistent return values
Right now, there are two possible return types: a list of lists [[]]
if degree >= 2
and a list []
otherwise. If you make the types of all possible return values the same, then any code that calls this function can be simpler because it only has to handle one data type. This function should construct the full triangle, so all return statements should return a list of lists representing the full triangle.
def basic_pascals(degree):
triangle = [[1], [1, 1]]
if degree == 0:
return [[1]]
elif degree == 1:
return [[1], [1, 1]]
while len(triangle) < degree + 1:
next_row = []
last_row = triangle[-1]
for item in zip(last_row, last_row[1:]):
next_row.append(sum(item))
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
Now, pascals_triangle()
doesn't need special cases for degrees 0 and 1.
5. Removing special cases.
Now that all return values return the same data type, do we even need the special cases for degree=0
and degree=1
? Let's delete the initial if
block and replace the initial value of triangle
with [[1]]
.
def basic_pascals(degree):
triangle = [[1]]
while len(triangle) < degree + 1:
next_row = []
last_row = triangle[-1]
for item in zip(last_row, last_row[1:]):
next_row.append(sum(item))
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
This still returns the correct answer.
6. Replace for ... append
with list comprehensions
The inner for loop can be expressed as a single line to make the full list. This is called a list comprehension.
def basic_pascals(degree):
triangle = [[1]]
while len(triangle) < degree + 1:
last_row = triangle[-1]
next_row = [sum(item) for item in zip(last_row, last_row[1:])]
next_row.append(1)
next_row.insert(0, 1)
triangle.append(next_row)
return triangle
We can even incorporate the ones on the start and end.
def basic_pascals(degree):
triangle = [[1]]
while len(triangle) < degree + 1:
last_row = triangle[-1]
next_row = [1] + [sum(item) for item in zip(last_row, last_row[1:])] + [1]
triangle.append(next_row)
return triangle
I added space before the return statement to emphasize the three steps: initialize triangle
, construct triangle
, return triangle
.
Other parts of the code
In pascals_triangle()
, in addition to making similar changes as described above, you can create a function that takes co_efficient
, power_a
, and power_b
as argument to make each term in the polynomial. This will simplify the loop and give you more freedom to get the notation correct.
Rather than trying to build the whole expression at once, it is simpler to build a list of terms (terms = ["1a³", "3a²b¹", "3a¹b²", "1b³"]
) and then use " + ".join(terms)
to create the full string. Then, you don't have to use strip()
at the end.