Python Automate the Boring Stuff Collatz exercise
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
7
down vote
favorite
I'm looking for some feedback on my code for the collatz function exercise in the Python book Automate the Boring Stuff. It works, but doesn't feel very elegant to me. Is there a better way to approach it? I'm trying to avoid just looking at someone else's solution so general advice would be preferred.
def collatz(number):
if (number %2 ==0 ) :#even
newNumber=number//2
print(newNumber);
if newNumber!=1:
collatz(newNumber);
else:
return (newNumber)
elif (number % 2 == 1) : #odd
newNumber=3*number+1
print (newNumber)
collatz(newNumber)
return newNumber;
#Input validation loop
while True:
try:
number = int(input("Enter number:"))
except ValueError:
print("Enter a valid integer")
continue
else:
if number < 1:
print("enter a valid positive integer")
continue
break
collatz(number)
python python-3.x collatz-sequence
add a comment |Â
up vote
7
down vote
favorite
I'm looking for some feedback on my code for the collatz function exercise in the Python book Automate the Boring Stuff. It works, but doesn't feel very elegant to me. Is there a better way to approach it? I'm trying to avoid just looking at someone else's solution so general advice would be preferred.
def collatz(number):
if (number %2 ==0 ) :#even
newNumber=number//2
print(newNumber);
if newNumber!=1:
collatz(newNumber);
else:
return (newNumber)
elif (number % 2 == 1) : #odd
newNumber=3*number+1
print (newNumber)
collatz(newNumber)
return newNumber;
#Input validation loop
while True:
try:
number = int(input("Enter number:"))
except ValueError:
print("Enter a valid integer")
continue
else:
if number < 1:
print("enter a valid positive integer")
continue
break
collatz(number)
python python-3.x collatz-sequence
add a comment |Â
up vote
7
down vote
favorite
up vote
7
down vote
favorite
I'm looking for some feedback on my code for the collatz function exercise in the Python book Automate the Boring Stuff. It works, but doesn't feel very elegant to me. Is there a better way to approach it? I'm trying to avoid just looking at someone else's solution so general advice would be preferred.
def collatz(number):
if (number %2 ==0 ) :#even
newNumber=number//2
print(newNumber);
if newNumber!=1:
collatz(newNumber);
else:
return (newNumber)
elif (number % 2 == 1) : #odd
newNumber=3*number+1
print (newNumber)
collatz(newNumber)
return newNumber;
#Input validation loop
while True:
try:
number = int(input("Enter number:"))
except ValueError:
print("Enter a valid integer")
continue
else:
if number < 1:
print("enter a valid positive integer")
continue
break
collatz(number)
python python-3.x collatz-sequence
I'm looking for some feedback on my code for the collatz function exercise in the Python book Automate the Boring Stuff. It works, but doesn't feel very elegant to me. Is there a better way to approach it? I'm trying to avoid just looking at someone else's solution so general advice would be preferred.
def collatz(number):
if (number %2 ==0 ) :#even
newNumber=number//2
print(newNumber);
if newNumber!=1:
collatz(newNumber);
else:
return (newNumber)
elif (number % 2 == 1) : #odd
newNumber=3*number+1
print (newNumber)
collatz(newNumber)
return newNumber;
#Input validation loop
while True:
try:
number = int(input("Enter number:"))
except ValueError:
print("Enter a valid integer")
continue
else:
if number < 1:
print("enter a valid positive integer")
continue
break
collatz(number)
python python-3.x collatz-sequence
edited May 21 at 8:33
Billal BEGUERADJ
1
1
asked May 21 at 2:11
user3342947
383
383
add a comment |Â
add a comment |Â
4 Answers
4
active
oldest
votes
up vote
6
down vote
accepted
Cleaning the user input validation
- Remove
continue
because in both cases the loop will continue anyway, onlybreak
will stop it, so do not worry. - Do not make the user think:
number = int(input("Enter number:"))
: which number to enter? Sure you know, but messages are intended to communicate with the user, so do not make him guess what you want, but be clear and precise with him.
Cleaning collatz()
- Please apply the naming conventions and indentation rules.
- Leave space between operators and their operands (example:
newNumber=3*number+1
should be writtennewNumber = 3 * number + 1
) - Do not borrow
;
sysntax into Python. You should remove it. - Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even. The same thing goes for#odd
and#Input validation loop
. - Remove
return newNumber
, it does not make sense becausereturn (newNumber)
is enough to exit the recursion whennewNumber == 1
.
So under the shadow of what is mentioned above, let us clean collatz()
:
def collatz(number):
if (number %2 == 0 ):
new_number = number // 2
print(new_number);
if new_number!=1:
collatz(new_number);
else:
return new_number
elif (number % 2 == 1):
new_number = 3 * number + 1
print (new_number)
collatz(new_number)
Improving the input validation
- What you have done so far is good, I suggest you to handle the case where the user presses Ctrl + c to exit your program, because when I tried to do so, I got an ugly
KeyboardInterrupt
exception. - Think of code reuse, I mean the input validation could be re-structured as a function, this way you can use it elsewhere easily if similar purposes are encountered.
Given these last 2 elements, let us re-write the code validation:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer number ( > 1): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
Improving collatz()
You said you do not want to see other solutions and you want only to see if you can improve your own one. So let us see if we can do so.
From your solution we can see:
- You always call
collatz()
whethernew_number
is odd or even. - You always print
new_number
whether it is odd or even - The only useful return statement is the once which corresponds to
new_number = 1
.
Let us translate the 3 phrases above into code:
callatz(new_number)
print(new_number)
return new_number
We should not forget the shared fact: new_number can be either odd or even (so we do not care about that)
new_number = n//2 if n % 2 == 0 else n*3 + 1
Now we are ready to gather the 4 instructions listed above into one following their coherent flow:
def collatz(n):
if n == 1:
return
else:
new_number = n // 2 if n % 2 == 0 else n * 3 + 1
print(new_number)
collatz(new_number)
Putting all together
Let us gather the code written so far to create an MCVE:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer (minimum 2): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
def collatz(n):
if n == 1:
return
else:
new_number = n//2 if n % 2 == 0 else n*3 + 1
print(new_number)
collatz(new_number)
if __name__ == '__main__':
print('Collatz Conjecture Collatz Conjecture')
number = validate_user_input()
collatz(number)
P.S. Just in case: you can read about if __name__ == "__main__":
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments:def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
add a comment |Â
up vote
8
down vote
Some quick thoughts:
- Linting the code will make it more easily readable to Python developers (including yourself when you get used to it).
elif (number % 2 == 1) :
could just beelse:
because a number which is not even must be odd.return (newNumber)
should be justreturn newNumber
.- The code below
#Input validation loop
should be in amain
function, calledif __name__ == '__main__'
. That way it's possible to import your function for reuse in other code. Rather than returning just the next number in the sequence you probably want to return an infinite iterator so that the caller can get more numbers trivially:
class Collatz():
def __init__(self, start):
self.number = start
def __iter__(self):
return self
def __next__(self):
if self.number in [0, 1]:
raise StopIteration()
if self.number % 2 == 0:
self.number = int(self.number / 2)
else:
self.number *= 3
self.number += 1
return self.numberUse:
>>> print(list(Collatz(15)))
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
add a comment |Â
up vote
2
down vote
To promote code reuse, you should design the collatz()
function so that it doesn't print anything. Functions should either perform computations or I/O, not both. Furthermore, there should be as little "free-floating" code as possible, and the input-prompting loop should be in a function. Ideally, the main body of the program should look like this:
for n in collatz(ask_positive_int()):
print(n)
Your collatz()
function works recursively. For long sequences, that will cause your program to crash due to stack overflow. It should be written using a loop instead.
To avoid mixing I/O with the computations, you should yield
the results instead of print()
ing them. That makes your function a Python generator.
Your function does not print the initial number. I find that behaviour to be a bit counterintuitive. It also complicates the code a bit, making you write two print(newNumber)
calls.
You don't need a newNumber
variable at all; you can just overwrite the number
parameter.
def collatz(n):
"""
Generate the Collatz sequence, starting from n, until 1 is reached.
The output starts with n and ends with 1.
"""
while True:
yield n
if n % 2 == 0:
n //= 2
elif n == 1:
break
else:
n = 3 * n + 1
def ask_positive_int():
"""Ask the user to enter a positive integer, retrying on invalid input."""
while True:
try:
n = int(input("Enter a positive integer: "))
if n < 1:
print("Number must be positive")
else:
return n
except ValueError:
print("Number must be an integer")
for n in collatz(ask_positive_int()):
print(n)
add a comment |Â
up vote
1
down vote
number%2
returns the remainder of number
when divided by 2. Python is not a strongly typed language, so this remainder can be used as a boolean; if there is a remainder, number%2
is truthy, and if the remainder is zero, then it is falsy. So you can replace your if (number %2 ==0 ) [even stuff] else [odd stuff]
with if (number %2) [odd stuff] else [even stuff]
.
if newNumber!=1:
collatz(newNumber);
These lines seem to be based on the assumption that all numbers eventually reach 1. While this has been verified for numbers you are likely to come across, it hasn't been proven for all numbers. You might want to instead check whether you've entered a loop. Also I think it's better to explicitly return whether than relying on Python returning the last line executed by default, so you should have return(collatz(newNumber))
. And get rid of the semicolon.
Also, you're repeating a lot of code. The only difference between the odd and even case is that in the former case you have 3*number+1
and in the latter case you have number//2
. The print
and return
statements are the same. So you can simplify your code to the following:
if number%2:
newNumber = 3*number+1
else:
newNumber = number//2
print(newNumber)
if newNumber == 1:
return(newNumber)
else:
return(collatz(newNumber))
return newNumber;
try:
number = int(input("Enter number:"))
Keep in mind that this will reject 5.0
.
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
Cleaning the user input validation
- Remove
continue
because in both cases the loop will continue anyway, onlybreak
will stop it, so do not worry. - Do not make the user think:
number = int(input("Enter number:"))
: which number to enter? Sure you know, but messages are intended to communicate with the user, so do not make him guess what you want, but be clear and precise with him.
Cleaning collatz()
- Please apply the naming conventions and indentation rules.
- Leave space between operators and their operands (example:
newNumber=3*number+1
should be writtennewNumber = 3 * number + 1
) - Do not borrow
;
sysntax into Python. You should remove it. - Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even. The same thing goes for#odd
and#Input validation loop
. - Remove
return newNumber
, it does not make sense becausereturn (newNumber)
is enough to exit the recursion whennewNumber == 1
.
So under the shadow of what is mentioned above, let us clean collatz()
:
def collatz(number):
if (number %2 == 0 ):
new_number = number // 2
print(new_number);
if new_number!=1:
collatz(new_number);
else:
return new_number
elif (number % 2 == 1):
new_number = 3 * number + 1
print (new_number)
collatz(new_number)
Improving the input validation
- What you have done so far is good, I suggest you to handle the case where the user presses Ctrl + c to exit your program, because when I tried to do so, I got an ugly
KeyboardInterrupt
exception. - Think of code reuse, I mean the input validation could be re-structured as a function, this way you can use it elsewhere easily if similar purposes are encountered.
Given these last 2 elements, let us re-write the code validation:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer number ( > 1): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
Improving collatz()
You said you do not want to see other solutions and you want only to see if you can improve your own one. So let us see if we can do so.
From your solution we can see:
- You always call
collatz()
whethernew_number
is odd or even. - You always print
new_number
whether it is odd or even - The only useful return statement is the once which corresponds to
new_number = 1
.
Let us translate the 3 phrases above into code:
callatz(new_number)
print(new_number)
return new_number
We should not forget the shared fact: new_number can be either odd or even (so we do not care about that)
new_number = n//2 if n % 2 == 0 else n*3 + 1
Now we are ready to gather the 4 instructions listed above into one following their coherent flow:
def collatz(n):
if n == 1:
return
else:
new_number = n // 2 if n % 2 == 0 else n * 3 + 1
print(new_number)
collatz(new_number)
Putting all together
Let us gather the code written so far to create an MCVE:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer (minimum 2): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
def collatz(n):
if n == 1:
return
else:
new_number = n//2 if n % 2 == 0 else n*3 + 1
print(new_number)
collatz(new_number)
if __name__ == '__main__':
print('Collatz Conjecture Collatz Conjecture')
number = validate_user_input()
collatz(number)
P.S. Just in case: you can read about if __name__ == "__main__":
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments:def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
add a comment |Â
up vote
6
down vote
accepted
Cleaning the user input validation
- Remove
continue
because in both cases the loop will continue anyway, onlybreak
will stop it, so do not worry. - Do not make the user think:
number = int(input("Enter number:"))
: which number to enter? Sure you know, but messages are intended to communicate with the user, so do not make him guess what you want, but be clear and precise with him.
Cleaning collatz()
- Please apply the naming conventions and indentation rules.
- Leave space between operators and their operands (example:
newNumber=3*number+1
should be writtennewNumber = 3 * number + 1
) - Do not borrow
;
sysntax into Python. You should remove it. - Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even. The same thing goes for#odd
and#Input validation loop
. - Remove
return newNumber
, it does not make sense becausereturn (newNumber)
is enough to exit the recursion whennewNumber == 1
.
So under the shadow of what is mentioned above, let us clean collatz()
:
def collatz(number):
if (number %2 == 0 ):
new_number = number // 2
print(new_number);
if new_number!=1:
collatz(new_number);
else:
return new_number
elif (number % 2 == 1):
new_number = 3 * number + 1
print (new_number)
collatz(new_number)
Improving the input validation
- What you have done so far is good, I suggest you to handle the case where the user presses Ctrl + c to exit your program, because when I tried to do so, I got an ugly
KeyboardInterrupt
exception. - Think of code reuse, I mean the input validation could be re-structured as a function, this way you can use it elsewhere easily if similar purposes are encountered.
Given these last 2 elements, let us re-write the code validation:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer number ( > 1): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
Improving collatz()
You said you do not want to see other solutions and you want only to see if you can improve your own one. So let us see if we can do so.
From your solution we can see:
- You always call
collatz()
whethernew_number
is odd or even. - You always print
new_number
whether it is odd or even - The only useful return statement is the once which corresponds to
new_number = 1
.
Let us translate the 3 phrases above into code:
callatz(new_number)
print(new_number)
return new_number
We should not forget the shared fact: new_number can be either odd or even (so we do not care about that)
new_number = n//2 if n % 2 == 0 else n*3 + 1
Now we are ready to gather the 4 instructions listed above into one following their coherent flow:
def collatz(n):
if n == 1:
return
else:
new_number = n // 2 if n % 2 == 0 else n * 3 + 1
print(new_number)
collatz(new_number)
Putting all together
Let us gather the code written so far to create an MCVE:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer (minimum 2): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
def collatz(n):
if n == 1:
return
else:
new_number = n//2 if n % 2 == 0 else n*3 + 1
print(new_number)
collatz(new_number)
if __name__ == '__main__':
print('Collatz Conjecture Collatz Conjecture')
number = validate_user_input()
collatz(number)
P.S. Just in case: you can read about if __name__ == "__main__":
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments:def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
add a comment |Â
up vote
6
down vote
accepted
up vote
6
down vote
accepted
Cleaning the user input validation
- Remove
continue
because in both cases the loop will continue anyway, onlybreak
will stop it, so do not worry. - Do not make the user think:
number = int(input("Enter number:"))
: which number to enter? Sure you know, but messages are intended to communicate with the user, so do not make him guess what you want, but be clear and precise with him.
Cleaning collatz()
- Please apply the naming conventions and indentation rules.
- Leave space between operators and their operands (example:
newNumber=3*number+1
should be writtennewNumber = 3 * number + 1
) - Do not borrow
;
sysntax into Python. You should remove it. - Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even. The same thing goes for#odd
and#Input validation loop
. - Remove
return newNumber
, it does not make sense becausereturn (newNumber)
is enough to exit the recursion whennewNumber == 1
.
So under the shadow of what is mentioned above, let us clean collatz()
:
def collatz(number):
if (number %2 == 0 ):
new_number = number // 2
print(new_number);
if new_number!=1:
collatz(new_number);
else:
return new_number
elif (number % 2 == 1):
new_number = 3 * number + 1
print (new_number)
collatz(new_number)
Improving the input validation
- What you have done so far is good, I suggest you to handle the case where the user presses Ctrl + c to exit your program, because when I tried to do so, I got an ugly
KeyboardInterrupt
exception. - Think of code reuse, I mean the input validation could be re-structured as a function, this way you can use it elsewhere easily if similar purposes are encountered.
Given these last 2 elements, let us re-write the code validation:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer number ( > 1): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
Improving collatz()
You said you do not want to see other solutions and you want only to see if you can improve your own one. So let us see if we can do so.
From your solution we can see:
- You always call
collatz()
whethernew_number
is odd or even. - You always print
new_number
whether it is odd or even - The only useful return statement is the once which corresponds to
new_number = 1
.
Let us translate the 3 phrases above into code:
callatz(new_number)
print(new_number)
return new_number
We should not forget the shared fact: new_number can be either odd or even (so we do not care about that)
new_number = n//2 if n % 2 == 0 else n*3 + 1
Now we are ready to gather the 4 instructions listed above into one following their coherent flow:
def collatz(n):
if n == 1:
return
else:
new_number = n // 2 if n % 2 == 0 else n * 3 + 1
print(new_number)
collatz(new_number)
Putting all together
Let us gather the code written so far to create an MCVE:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer (minimum 2): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
def collatz(n):
if n == 1:
return
else:
new_number = n//2 if n % 2 == 0 else n*3 + 1
print(new_number)
collatz(new_number)
if __name__ == '__main__':
print('Collatz Conjecture Collatz Conjecture')
number = validate_user_input()
collatz(number)
P.S. Just in case: you can read about if __name__ == "__main__":
Cleaning the user input validation
- Remove
continue
because in both cases the loop will continue anyway, onlybreak
will stop it, so do not worry. - Do not make the user think:
number = int(input("Enter number:"))
: which number to enter? Sure you know, but messages are intended to communicate with the user, so do not make him guess what you want, but be clear and precise with him.
Cleaning collatz()
- Please apply the naming conventions and indentation rules.
- Leave space between operators and their operands (example:
newNumber=3*number+1
should be writtennewNumber = 3 * number + 1
) - Do not borrow
;
sysntax into Python. You should remove it. - Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even. The same thing goes for#odd
and#Input validation loop
. - Remove
return newNumber
, it does not make sense becausereturn (newNumber)
is enough to exit the recursion whennewNumber == 1
.
So under the shadow of what is mentioned above, let us clean collatz()
:
def collatz(number):
if (number %2 == 0 ):
new_number = number // 2
print(new_number);
if new_number!=1:
collatz(new_number);
else:
return new_number
elif (number % 2 == 1):
new_number = 3 * number + 1
print (new_number)
collatz(new_number)
Improving the input validation
- What you have done so far is good, I suggest you to handle the case where the user presses Ctrl + c to exit your program, because when I tried to do so, I got an ugly
KeyboardInterrupt
exception. - Think of code reuse, I mean the input validation could be re-structured as a function, this way you can use it elsewhere easily if similar purposes are encountered.
Given these last 2 elements, let us re-write the code validation:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer number ( > 1): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
Improving collatz()
You said you do not want to see other solutions and you want only to see if you can improve your own one. So let us see if we can do so.
From your solution we can see:
- You always call
collatz()
whethernew_number
is odd or even. - You always print
new_number
whether it is odd or even - The only useful return statement is the once which corresponds to
new_number = 1
.
Let us translate the 3 phrases above into code:
callatz(new_number)
print(new_number)
return new_number
We should not forget the shared fact: new_number can be either odd or even (so we do not care about that)
new_number = n//2 if n % 2 == 0 else n*3 + 1
Now we are ready to gather the 4 instructions listed above into one following their coherent flow:
def collatz(n):
if n == 1:
return
else:
new_number = n // 2 if n % 2 == 0 else n * 3 + 1
print(new_number)
collatz(new_number)
Putting all together
Let us gather the code written so far to create an MCVE:
def validate_user_input():
while True:
try:
number = int(input("Enter a positive integer (minimum 2): "))
except KeyboardInterrupt:
print('nSee you next time!')
break
except ValueError:
print("That was not even a valid integer!")
else:
if number < 0:
print("Positive integer, please!")
elif number < 2:
print("Integer should be at least equal to 2 !")
else:
return number
def collatz(n):
if n == 1:
return
else:
new_number = n//2 if n % 2 == 0 else n*3 + 1
print(new_number)
collatz(new_number)
if __name__ == '__main__':
print('Collatz Conjecture Collatz Conjecture')
number = validate_user_input()
collatz(number)
P.S. Just in case: you can read about if __name__ == "__main__":
edited May 21 at 10:06
Daniel
4,1132836
4,1132836
answered May 21 at 8:31
Billal BEGUERADJ
1
1
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments:def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
add a comment |Â
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case wherenumber
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments:def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
1
1
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
Thanks heaps, I will work through your suggestions. I'm just getting started with python after only doing a small amount of programming in highschool many years ago, so I want to try to build good habits from the start.
â user3342947
May 21 at 9:55
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
wow, just gave it a read. Thank you, everything is explained well, I will work through my program and try to apply everything to the next exercises I do as well, thank you very much
â user3342947
May 21 at 10:04
"Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case where number
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments: def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
"Remove unnecessary comments: when you code
(number %2 ==0 ) :#even
, I think everybody is smart enough to guess you are processing the case where number
is even." â And if you do deem it necessary to explain what this expression does, do it with names, not comments: def isEven(n): return n % 2 == 0
â Jörg W Mittag
May 21 at 11:00
add a comment |Â
up vote
8
down vote
Some quick thoughts:
- Linting the code will make it more easily readable to Python developers (including yourself when you get used to it).
elif (number % 2 == 1) :
could just beelse:
because a number which is not even must be odd.return (newNumber)
should be justreturn newNumber
.- The code below
#Input validation loop
should be in amain
function, calledif __name__ == '__main__'
. That way it's possible to import your function for reuse in other code. Rather than returning just the next number in the sequence you probably want to return an infinite iterator so that the caller can get more numbers trivially:
class Collatz():
def __init__(self, start):
self.number = start
def __iter__(self):
return self
def __next__(self):
if self.number in [0, 1]:
raise StopIteration()
if self.number % 2 == 0:
self.number = int(self.number / 2)
else:
self.number *= 3
self.number += 1
return self.numberUse:
>>> print(list(Collatz(15)))
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
add a comment |Â
up vote
8
down vote
Some quick thoughts:
- Linting the code will make it more easily readable to Python developers (including yourself when you get used to it).
elif (number % 2 == 1) :
could just beelse:
because a number which is not even must be odd.return (newNumber)
should be justreturn newNumber
.- The code below
#Input validation loop
should be in amain
function, calledif __name__ == '__main__'
. That way it's possible to import your function for reuse in other code. Rather than returning just the next number in the sequence you probably want to return an infinite iterator so that the caller can get more numbers trivially:
class Collatz():
def __init__(self, start):
self.number = start
def __iter__(self):
return self
def __next__(self):
if self.number in [0, 1]:
raise StopIteration()
if self.number % 2 == 0:
self.number = int(self.number / 2)
else:
self.number *= 3
self.number += 1
return self.numberUse:
>>> print(list(Collatz(15)))
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
add a comment |Â
up vote
8
down vote
up vote
8
down vote
Some quick thoughts:
- Linting the code will make it more easily readable to Python developers (including yourself when you get used to it).
elif (number % 2 == 1) :
could just beelse:
because a number which is not even must be odd.return (newNumber)
should be justreturn newNumber
.- The code below
#Input validation loop
should be in amain
function, calledif __name__ == '__main__'
. That way it's possible to import your function for reuse in other code. Rather than returning just the next number in the sequence you probably want to return an infinite iterator so that the caller can get more numbers trivially:
class Collatz():
def __init__(self, start):
self.number = start
def __iter__(self):
return self
def __next__(self):
if self.number in [0, 1]:
raise StopIteration()
if self.number % 2 == 0:
self.number = int(self.number / 2)
else:
self.number *= 3
self.number += 1
return self.numberUse:
>>> print(list(Collatz(15)))
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Some quick thoughts:
- Linting the code will make it more easily readable to Python developers (including yourself when you get used to it).
elif (number % 2 == 1) :
could just beelse:
because a number which is not even must be odd.return (newNumber)
should be justreturn newNumber
.- The code below
#Input validation loop
should be in amain
function, calledif __name__ == '__main__'
. That way it's possible to import your function for reuse in other code. Rather than returning just the next number in the sequence you probably want to return an infinite iterator so that the caller can get more numbers trivially:
class Collatz():
def __init__(self, start):
self.number = start
def __iter__(self):
return self
def __next__(self):
if self.number in [0, 1]:
raise StopIteration()
if self.number % 2 == 0:
self.number = int(self.number / 2)
else:
self.number *= 3
self.number += 1
return self.numberUse:
>>> print(list(Collatz(15)))
[46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
edited May 21 at 10:30
answered May 21 at 8:09
l0b0
3,580922
3,580922
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
add a comment |Â
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
Thanks for the reply. Could you expand on number 4? everything else looks like good suggestions but I can't quite work out what you mean by an infinite iterator
â user3342947
May 21 at 9:58
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
thanks, that's pretty cool I haven't come across that before
â user3342947
May 21 at 10:08
3
3
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
instead of an iterator, consider using a generator function as it should be easier to read and write
â WorldSEnder
May 21 at 11:05
add a comment |Â
up vote
2
down vote
To promote code reuse, you should design the collatz()
function so that it doesn't print anything. Functions should either perform computations or I/O, not both. Furthermore, there should be as little "free-floating" code as possible, and the input-prompting loop should be in a function. Ideally, the main body of the program should look like this:
for n in collatz(ask_positive_int()):
print(n)
Your collatz()
function works recursively. For long sequences, that will cause your program to crash due to stack overflow. It should be written using a loop instead.
To avoid mixing I/O with the computations, you should yield
the results instead of print()
ing them. That makes your function a Python generator.
Your function does not print the initial number. I find that behaviour to be a bit counterintuitive. It also complicates the code a bit, making you write two print(newNumber)
calls.
You don't need a newNumber
variable at all; you can just overwrite the number
parameter.
def collatz(n):
"""
Generate the Collatz sequence, starting from n, until 1 is reached.
The output starts with n and ends with 1.
"""
while True:
yield n
if n % 2 == 0:
n //= 2
elif n == 1:
break
else:
n = 3 * n + 1
def ask_positive_int():
"""Ask the user to enter a positive integer, retrying on invalid input."""
while True:
try:
n = int(input("Enter a positive integer: "))
if n < 1:
print("Number must be positive")
else:
return n
except ValueError:
print("Number must be an integer")
for n in collatz(ask_positive_int()):
print(n)
add a comment |Â
up vote
2
down vote
To promote code reuse, you should design the collatz()
function so that it doesn't print anything. Functions should either perform computations or I/O, not both. Furthermore, there should be as little "free-floating" code as possible, and the input-prompting loop should be in a function. Ideally, the main body of the program should look like this:
for n in collatz(ask_positive_int()):
print(n)
Your collatz()
function works recursively. For long sequences, that will cause your program to crash due to stack overflow. It should be written using a loop instead.
To avoid mixing I/O with the computations, you should yield
the results instead of print()
ing them. That makes your function a Python generator.
Your function does not print the initial number. I find that behaviour to be a bit counterintuitive. It also complicates the code a bit, making you write two print(newNumber)
calls.
You don't need a newNumber
variable at all; you can just overwrite the number
parameter.
def collatz(n):
"""
Generate the Collatz sequence, starting from n, until 1 is reached.
The output starts with n and ends with 1.
"""
while True:
yield n
if n % 2 == 0:
n //= 2
elif n == 1:
break
else:
n = 3 * n + 1
def ask_positive_int():
"""Ask the user to enter a positive integer, retrying on invalid input."""
while True:
try:
n = int(input("Enter a positive integer: "))
if n < 1:
print("Number must be positive")
else:
return n
except ValueError:
print("Number must be an integer")
for n in collatz(ask_positive_int()):
print(n)
add a comment |Â
up vote
2
down vote
up vote
2
down vote
To promote code reuse, you should design the collatz()
function so that it doesn't print anything. Functions should either perform computations or I/O, not both. Furthermore, there should be as little "free-floating" code as possible, and the input-prompting loop should be in a function. Ideally, the main body of the program should look like this:
for n in collatz(ask_positive_int()):
print(n)
Your collatz()
function works recursively. For long sequences, that will cause your program to crash due to stack overflow. It should be written using a loop instead.
To avoid mixing I/O with the computations, you should yield
the results instead of print()
ing them. That makes your function a Python generator.
Your function does not print the initial number. I find that behaviour to be a bit counterintuitive. It also complicates the code a bit, making you write two print(newNumber)
calls.
You don't need a newNumber
variable at all; you can just overwrite the number
parameter.
def collatz(n):
"""
Generate the Collatz sequence, starting from n, until 1 is reached.
The output starts with n and ends with 1.
"""
while True:
yield n
if n % 2 == 0:
n //= 2
elif n == 1:
break
else:
n = 3 * n + 1
def ask_positive_int():
"""Ask the user to enter a positive integer, retrying on invalid input."""
while True:
try:
n = int(input("Enter a positive integer: "))
if n < 1:
print("Number must be positive")
else:
return n
except ValueError:
print("Number must be an integer")
for n in collatz(ask_positive_int()):
print(n)
To promote code reuse, you should design the collatz()
function so that it doesn't print anything. Functions should either perform computations or I/O, not both. Furthermore, there should be as little "free-floating" code as possible, and the input-prompting loop should be in a function. Ideally, the main body of the program should look like this:
for n in collatz(ask_positive_int()):
print(n)
Your collatz()
function works recursively. For long sequences, that will cause your program to crash due to stack overflow. It should be written using a loop instead.
To avoid mixing I/O with the computations, you should yield
the results instead of print()
ing them. That makes your function a Python generator.
Your function does not print the initial number. I find that behaviour to be a bit counterintuitive. It also complicates the code a bit, making you write two print(newNumber)
calls.
You don't need a newNumber
variable at all; you can just overwrite the number
parameter.
def collatz(n):
"""
Generate the Collatz sequence, starting from n, until 1 is reached.
The output starts with n and ends with 1.
"""
while True:
yield n
if n % 2 == 0:
n //= 2
elif n == 1:
break
else:
n = 3 * n + 1
def ask_positive_int():
"""Ask the user to enter a positive integer, retrying on invalid input."""
while True:
try:
n = int(input("Enter a positive integer: "))
if n < 1:
print("Number must be positive")
else:
return n
except ValueError:
print("Number must be an integer")
for n in collatz(ask_positive_int()):
print(n)
answered May 21 at 17:49
200_success
123k14143399
123k14143399
add a comment |Â
add a comment |Â
up vote
1
down vote
number%2
returns the remainder of number
when divided by 2. Python is not a strongly typed language, so this remainder can be used as a boolean; if there is a remainder, number%2
is truthy, and if the remainder is zero, then it is falsy. So you can replace your if (number %2 ==0 ) [even stuff] else [odd stuff]
with if (number %2) [odd stuff] else [even stuff]
.
if newNumber!=1:
collatz(newNumber);
These lines seem to be based on the assumption that all numbers eventually reach 1. While this has been verified for numbers you are likely to come across, it hasn't been proven for all numbers. You might want to instead check whether you've entered a loop. Also I think it's better to explicitly return whether than relying on Python returning the last line executed by default, so you should have return(collatz(newNumber))
. And get rid of the semicolon.
Also, you're repeating a lot of code. The only difference between the odd and even case is that in the former case you have 3*number+1
and in the latter case you have number//2
. The print
and return
statements are the same. So you can simplify your code to the following:
if number%2:
newNumber = 3*number+1
else:
newNumber = number//2
print(newNumber)
if newNumber == 1:
return(newNumber)
else:
return(collatz(newNumber))
return newNumber;
try:
number = int(input("Enter number:"))
Keep in mind that this will reject 5.0
.
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
add a comment |Â
up vote
1
down vote
number%2
returns the remainder of number
when divided by 2. Python is not a strongly typed language, so this remainder can be used as a boolean; if there is a remainder, number%2
is truthy, and if the remainder is zero, then it is falsy. So you can replace your if (number %2 ==0 ) [even stuff] else [odd stuff]
with if (number %2) [odd stuff] else [even stuff]
.
if newNumber!=1:
collatz(newNumber);
These lines seem to be based on the assumption that all numbers eventually reach 1. While this has been verified for numbers you are likely to come across, it hasn't been proven for all numbers. You might want to instead check whether you've entered a loop. Also I think it's better to explicitly return whether than relying on Python returning the last line executed by default, so you should have return(collatz(newNumber))
. And get rid of the semicolon.
Also, you're repeating a lot of code. The only difference between the odd and even case is that in the former case you have 3*number+1
and in the latter case you have number//2
. The print
and return
statements are the same. So you can simplify your code to the following:
if number%2:
newNumber = 3*number+1
else:
newNumber = number//2
print(newNumber)
if newNumber == 1:
return(newNumber)
else:
return(collatz(newNumber))
return newNumber;
try:
number = int(input("Enter number:"))
Keep in mind that this will reject 5.0
.
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
add a comment |Â
up vote
1
down vote
up vote
1
down vote
number%2
returns the remainder of number
when divided by 2. Python is not a strongly typed language, so this remainder can be used as a boolean; if there is a remainder, number%2
is truthy, and if the remainder is zero, then it is falsy. So you can replace your if (number %2 ==0 ) [even stuff] else [odd stuff]
with if (number %2) [odd stuff] else [even stuff]
.
if newNumber!=1:
collatz(newNumber);
These lines seem to be based on the assumption that all numbers eventually reach 1. While this has been verified for numbers you are likely to come across, it hasn't been proven for all numbers. You might want to instead check whether you've entered a loop. Also I think it's better to explicitly return whether than relying on Python returning the last line executed by default, so you should have return(collatz(newNumber))
. And get rid of the semicolon.
Also, you're repeating a lot of code. The only difference between the odd and even case is that in the former case you have 3*number+1
and in the latter case you have number//2
. The print
and return
statements are the same. So you can simplify your code to the following:
if number%2:
newNumber = 3*number+1
else:
newNumber = number//2
print(newNumber)
if newNumber == 1:
return(newNumber)
else:
return(collatz(newNumber))
return newNumber;
try:
number = int(input("Enter number:"))
Keep in mind that this will reject 5.0
.
number%2
returns the remainder of number
when divided by 2. Python is not a strongly typed language, so this remainder can be used as a boolean; if there is a remainder, number%2
is truthy, and if the remainder is zero, then it is falsy. So you can replace your if (number %2 ==0 ) [even stuff] else [odd stuff]
with if (number %2) [odd stuff] else [even stuff]
.
if newNumber!=1:
collatz(newNumber);
These lines seem to be based on the assumption that all numbers eventually reach 1. While this has been verified for numbers you are likely to come across, it hasn't been proven for all numbers. You might want to instead check whether you've entered a loop. Also I think it's better to explicitly return whether than relying on Python returning the last line executed by default, so you should have return(collatz(newNumber))
. And get rid of the semicolon.
Also, you're repeating a lot of code. The only difference between the odd and even case is that in the former case you have 3*number+1
and in the latter case you have number//2
. The print
and return
statements are the same. So you can simplify your code to the following:
if number%2:
newNumber = 3*number+1
else:
newNumber = number//2
print(newNumber)
if newNumber == 1:
return(newNumber)
else:
return(collatz(newNumber))
return newNumber;
try:
number = int(input("Enter number:"))
Keep in mind that this will reject 5.0
.
edited May 21 at 18:27
answered May 21 at 15:45
Acccumulation
9395
9395
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
add a comment |Â
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
1
1
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
@200_success "Perhaps you got it confused with the print statement" That is likely.
â Acccumulation
May 21 at 18:28
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194837%2fpython-automate-the-boring-stuff-collatz-exercise%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password