# [Tutor] How to separate UI code from program logic?

boB Stepp robertvstepp at gmail.com
Mon May 7 01:26:08 EDT 2018

```On Sun, May 6, 2018 at 5:05 PM, Alan Gauld <alan.gauld at yahoo.co.uk> wrote:
> On 6 May 2018, at 23:00, boB Stepp <robertvstepp at gmail.com> wrote:

>>My understanding of best practice here is that I should not have any
>>print() calls inside my generate_collatz_sequence() function.  I
>>_could_ store the generated sequence in a list and return it, but that
>>does not seem like good use of RAM if some user-supplied seed value
>>led to kazillions of Collatz sequence numbers being generated.
>
> The clue is in that last word.
> Write a generator function that yields the intermediate results.

Ok.  I've been reading up on generators and playing around with them
today.  And then, per Alan's hint, tried to separate all display code
from my program logic code.  What was originally meant to be a
throw-away effort, has somehow mushroomed into something I hope
resembles a finished product.  The results I came up with are as
follows:

#--------------------------------------------------------------------------------

#!/usr/bin/env python3

"""This program will generate a Collatz sequence from a user-supplied positive
integer.  According to Wikipedia
(https://en.wikipedia.org/wiki/Collatz_conjecture):

"The Collatz conjecture is a conjecture in mathematics that concerns a
sequence defined as follows: start with any positive integer n. Then each
term is obtained from the previous term as follows: if the previous term is
even, the next term is one half the previous term. Otherwise, the next term
is 3 times the previous term plus 1. The conjecture is that no matter what
value of n, the sequence will always reach 1."
"""

def get_positive_integer():
"""Get a positive integer from the user."""

while True:
try:
integer = int(input("Please enter a positive integer:  "))
if integer > 0:
return integer
else:
print("That was not a positive integer!")
continue
except ValueError:
print("That was not an integer!")
continue

def display_collatz_numbers(seed, collatz_generator):
"""Display a Collatz sequence, one value per line, given the seed (Which
will be the first number in the sequence.) and a generator which will
yield the Collatz sequence."""

print("\nThe seed number: ", seed)
for sequence_index, number in enumerate(collatz_generator):
print("Sequence number", sequence_index + 1, ": ", number)

choice = input("Do you wish to generate another Collatz sequence?").lower()
return choice.startswith('y')

def get_collatz_number(integer):
"""Returns the Collatz sequence number corresponding to integer.  integer
must be > 0, or the sequence will not converge to 1."""

if integer % 2 == 0:
return integer // 2
else:
return 3 * integer + 1

def generate_collatz_sequence(seed):
"""Creates a generator, which will yield a Collatz sequence starting from
seed.  seed must be a positive integer, or the sequence will not converge to
1."""

collatz_number = seed
while True:
collatz_number = get_collatz_number(collatz_number)
yield collatz_number
if collatz_number == 1:
return

def main():
"""Run program."""

while True:
seed = get_positive_integer()
display_collatz_numbers(seed, generate_collatz_sequence(seed))
continue
else:
break

if __name__ == '__main__':
main()

#--------------------------------------------------------------------------------

1)  I am open to a general critique on making this code better.

2)  I spent a lot of effort trying to come up with a way to combine
the two functions, get_collatz_number() and
generate_collatz_sequence(), into something both more compact and more
readable, but I was unsuccessful.  I suspect there is a better way.
Is there?  And how would I do it?

3)  Is this the correct way to separate display code from program
logic code?  Is there a better way to do this?

4)  I think this is the first time I've actually tried to implement a
generator function.  Did I do a good Pythonic implementation of this?

As always, thanks!

--
boB
```