Engineering #9: factorials with loops, and randrange confusion

Exercise 9 of Launch School’s Loops and Iterating chapter, Introduction to Programming with Python book:
Write a function that computes and returns the factorial of a number by using a for or while loop. The factorial of a positive integer n, signified by n!, is defined as the product of all integers between 1 and n, inclusive. You may assume that the argument is always a positive integer.
n! Expansion Result
1! 1 1
2! 1 * 2 2
3! 1 * 2 * 3 6
4! 1 * 2 * 3 * 4 24
5! 1 * 2 * 3 * 4 * 5 120
For the while loop, I wanted to make a counter, of course; it’s like 2% of all the code I know right now. This works:
def factorial(num):
result = 1
counter = 1
while counter <= num:
result *= counter
counter += 1
return result
But the Launch School while loop is probably better:
def factorial(num):
result = 1
while num > 0:
result *= num
num -= 1
return result
Subtracting ends up as the cleanest code in this instance. And in the for loop, too:
def factorial(num):
result = 1
for number in range(num, 0, -1):
result *= number
return result
I’m not yet used to plugging in a range into a function like this. It surprised me, but ends up being super tidy.
And exercise 10:
The following code uses the randrange function from Python's random library to obtain and print a random integer within a given range. Using a while loop, it keeps running until it finds a random number that matches the last number in the range. Refactor the code so it doesn't require two different invocations of randrange.
import random
highest = 10
number = random.randrange(highest + 1)
print(number)
while number != highest:
number = random.randrange(highest + 1)
print(number)
I read this for six minutes. “Finds a random number that matches the last number in the range?” And then what happens? And the + 1 is due to the usual Python stuff?
I thought randrange had a number stowed and was running until it matched that number. No: “last number in the range” means the highest/largest number in the range, in this case 10, which we get randrange to access by adding + 1 to “highest”, since Python range functions don’t include the upper bound.
And this is the refactored solution:
import random
highest = 10
while True:
number = random.randrange(highest + 1)
print(number)
if number == highest:
break
This also showcases how you can use “while True” to be a state of “everything, until…” There is no competing False condition here. Cool to see.