< writing /
3 Nov 2017 | Rochester, NY
It is very easy to write bad code if you aren't careful. Having learned better practices for Python mostly by trial and error in my time as a researcher, I have returned to old scripts and been appalled by their lack of usability. They don't offer the future thinking flexibility that my more recent code does.
I examined the progress of my code and noticed a particular structure that emerged as I became a better programmer. Now, I use that structure as a starting point when I need to write a quick script. It helps me to write code that still makes sense when I come back to it several months later. This structure has three parts:
This structure is not by any means new, but I find it helpful to codify it as a sort of checklist to help make my code more usable. This structure is powerful because it separates the code into parts that are often modified on their own. When I edit an older code, my goal is either to change the data it uses, how it uses the data, or which functions act on the data. This keeps those parts from getting tangled.
To demonstrate this structure, I chose to solve a common interview question called FizzBuzz. I learned about it through Tom Scott's video on the problem which was originally posed in a blog post by Imran Ghory. Ghory suggests asking interviewees to write code that solves the following problem:
Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”. Although the head comes first in the final product, I most often begin writing code with the functions. After all, it is the functions themselves that really solve the problem.
It is better to approach writing the functions from a generalized standpoint. We could write two functions for this problem. One that prints ‘Fizz’ for multiples of 3 and one that prints ‘Buzz’ for multiples of 5, but the problems posed by both are so similar that they can both be handled by one function that prints a string for multiples of some number. That is nice and general and we can expand it for future use. Because this accomplishes our only task for the problem, we have the following as our complete set of Functions:
# Functions def string_to_print(number): out = '' for pair in numbers_to_check: if number%pair == 0: out += pair if out == '': return str(number) else: return out
This takes in a number and references a list of tuples that we will write into the Head. The function checks whether the input number is a multiple of any of the 0th values in the list of tuples. If it is, the function appends the 1st value in that tuple to an empty string called "out". Once all of the keys in the dictionary have been checked, if the number is not a multiple of any of them, the number itself is returned as a string to be printed. If the number is a multiple of any of the keys, the string "out" is returned.
Now we write the Head to give the function something to work with. The function references a list of tuples to determine which multiples to check and what to print. All we need to do is create that object:
# Head numbers_to_check = [ (3, 'Fizz'), (5, 'Buzz') ]
Finally we write Control. Here we can define the range over which we will count:
# Control def main(): for i in range(1,101): print(string_to_print(i)) main()
The only thing to note here is that because we want to check all numbers from 1 to 100, we have to set the end point of our range to 101 because the loop will count up to the terminal value, but not include it.
Altogether, the code reads
# Head numbers_to_check = [ (3, 'Fizz'), (5, 'Buzz') ] # Functions def string_to_print(number): out = '' for pair in numbers_to_check: if number%pair == 0: out += pair if out == '': return str(number) else: return out # Control def main(): for i in range(1,101): print(string_to_print(i)) main()
I think this is a much more readable way to write scripts, especially when they get very long because you always know where things are. You also have less of a chance of breaking the code by editing it. If we wanted to change this code to print "Lava" on multiples of 27, we just need to change the dictionary:
# Head numbers_to_check = [ (27, 'Lava') ]