|From Get Programming by Ana Bell
What does baking bread and writing code have in common? More than one might think…
Suppose I asked you to bake me a loaf of bread. What’s the process you go through from the time I give you the task to the point when you give me the finished loaf?
Understand the Task “bake a loaf of bread”
The first step is to make sure you understand the given task. “Bake a loaf of bread” is vague. Here are questions you should ask to clarify the task:
- What size of bread?
- Should it be a simple bread or flavored bread? Are there any specific ingredients you have to use or avoid using? Are there any ingredients you don’t have?
- What equipment do you need? Is the equipment supplied to you or do you need to get your own?
- Is there a time limit?
- Are there any recipes that you can look up and use or do you have to make one up on your own?
It‘s important that you get these details right to avoid having to start the task again. If there are no further details on the task, then the solution you come up with should be as simple as possible, and require as little work as possible: Look up a simple recipe instead of trying to come up with the correct combination of ingredients on your own, bake a small loaf of bread, don’t add any flavors or spices, and use a bread machine (if you have one) to save time.
Once you clarify any questions or misconceptions on the task, you can look up a recipe or come up with one on your own. The recipe tells you how to do the task. Coming up with a recipe on your own is the hardest part of doing the task. Once you have a recipe to follow, putting everything together shouldn’t be difficult.
Take a quick look at any recipe right now. Figure 1 shows a sample recipe. It includes
- What steps you should do and in what order
- Specific measurements
- Instructions on when and how many times to repeat a task
- What substitutions you can make for certain ingredients
- Any finishing touches on the dish and how to serve it
Figure 1. A sample recipe for bread.
The recipe is a sequence of steps that you must follow to bake bread. The steps are sequential; for example, you cannot take the loaf out of the oven without first putting the ingredients in the pan. At certain steps you can choose to put in one item instead of another, like either butter or canola oil, but not both. Some steps may be repeated, like occasionally checking for the crust color before declaring that it’s done.
Visualizing the Recipe with Flowcharts
When you read a recipe, the sequence of steps is likely outlined in words. To prepare you for understanding how to be a programmer, you should start to think about visualizing recipes with flowcharts. Figure 2 shows how to represent baking a bread with a flowchart. You represent steps in rectangular boxes. If a recipe allows for a possible substitution, represent that with a diamond box. If a recipe has you repeating a task, draw an arrow going back up to the first step in the repeated sequence.
Figure 2. A flowchart of a simple recipe for baking bread. Rectangular boxes represent doing an action. Grey diamonds represent a decision point. The dashed line represents going back up to a previous step to repeat a sequence. Follow arrows to trace paths through different implementations of the recipe.
Use an Existing Recipe or Make One Up?
Many recipes for bread are out there. How do you know which one to use? With a vague problem statement such as “bake a loaf of bread,” all are good to use because they all accomplish the task. In that sense, a more general problem statement’s easier for you when you have a set of recipes to pick from because any of the recipes will work.
If you have a picky eater and are asked to bake something for which there’s no recipe, you’ll have a hard time accomplishing the task. You have to experiment with different ingredient combinations and quantities, and with various temperatures and baking times. Likely, you’ll have to start over a few times.
The most common type of problem you’ll be given is a specific task for which you have partial information, such as “bake me a two-pound rosemary loaf of bread using 4 cups of flour, 1 tablespoon of sugar, 1 tablespoon of butter, 1 teaspoon of salt, and 1 teaspoon of yeast”. Here, you’re given a lot of critical information already in the task, but are missing a key piece; you have all the ingredient measurements except for the rosemary amount. Now, the hardest part is to experiment with ways of putting the ingredients together and to determine how much rosemary to add. If this isn’t your first time baking, then you come into the task with some intuition for how much rosemary is needed. The more practice you have, the easier it is for you.
The main idea to understand from the baking example is there’s more to baking than following a recipe. You have to first understand what you’re being asked to bake. Then you must determine if you have any existing recipes you can follow. If not, you’ll have to invent your own recipe and experiment until you have a final product that matches what was asked of you. In the next section, you’ll see how the baking example translates to programming.
Think, Code, Test, Debug, Repeat
As a programmer, you’ll write both simple and complicated programs. No matter what the complexity of the program, it’s important to approach every problem in an organized and structured manner. I suggest using the think-code-test-debug paradigm shown in Figure 3 until you’re satisfied your code works according to the problem specification.
The “think” step is equivalent to making sure you understand what kind of baked good you’re being asked to make. Think about the problem asked of you and decide if there are any recipes that might work or if you need to invent one on your own. In programming, recipes are algorithms.
The “code” step is equivalent to getting your hands dirty and experimenting with different possible combinations of ingredients, any substitutions, and any repetitive parts (for example, check the crust every five minutes). In programming, you’re coding up an implementation of an algorithm.
The “test” step is equivalent to determining whether the final product matches the expectations. For example, is the baked good that came out of the oven a loaf of bread? In programming, you run a program with different inputs and check if the actual output matches the expected output.
The “debug” step is equivalent to tweaking your recipe. For example, if it’s too salty, reduce the amount of salt you add. In programming, you debug a program to figure out which lines of code are causing incorrect behavior. This is a rough process if you don’t follow some best practices.
Figure 3 The ideal way to approach solving a problem with programming. Understand the problem before you write any code. Then test the code that you write and debug it as necessary.
Understanding the Task
When you’re given a problem that requires programming to solve, never write code right away. If you start by writing code, you enter the cycle directly at the “code” leg shown in Figure 3. It’s extremely unlikely you’ll write your code correctly the first time. That means you’ll have to cycle through until you think about the given problem, because you didn’t correctly solve it the first time. By thinking about the problem at the start, you minimize the number of times you’ll go through the programming cycle.
As you’re tackling harder and harder problems, it’s important that you try to break them into smaller problems with a simpler and smaller set of steps. You can focus on solving the smaller problems first. For example, instead of baking a loaf of bread with some exotic ingredients, try a few small rolls to get the proportions right without wasting too many resources or too much time.
When you’re given a problem, you should ask yourself
- What is this program supposed to accomplish? For example, “Find the area of a circle.”
- Are there any interactions with the user? For example, “The user will enter a number” and “You show the user the area of a circle with that radius.”
- What type of input is the user giving you? For example, “The user will give you a number representing the radius of a circle.”
- What does the user want from the program and in what form? For example, you might show the user “12.57” or you might be more verbose and show “The area of a circle with radius 2 is 12.57” or you might draw a picture for them.
Try This: Describe the Problem
Find any recipe (on a box or look one up on the internet). Write out a problem statement for what the recipe is trying to achieve. Write a vague problem statement. Write a more specific problem statement.
Problem – make mac-and-cheese.
Vague statement – dump box of mac-and-cheese in boiling water for 12 minutes.
Specific statement – pour 6 cups of water in a pot, turn up stove top temp and wait for water to boil, pour in noodles, let them cook for 12 minutes, drain noodles and add packet of cheese, stir.
Organize your thoughts on the problem by re-describing the problem in two ways:
- Visualize the problem.
- Write down a few sample inputs and what you expect the outputs to be.
Visualizing the Task
When you’re given a task to solve using programming, think of the task as a black box. At first, don’t worry about the implementation.
Definition An implementation is how you write the code to solve a task.
Instead of worrying about the details of the implementation, think about what’s being asked: Are there any interactions with the user? Is there any input your program might need? Is there any output your program might show? Is your program supposed to do calculations behind the scenes?
It’s helpful to draw a diagram showing possible interactions between your program and the user of the program. Go back to the bread example. A possible black box visualization is shown in Figure 4. The inputs are represented to the left of the black box and the outputs to the right.
Figure 4. A black box visualization of baking a loaf of bread with a given set of ingredients.
Once you have an idea of the inputs and outputs to your black box, think about any special behaviors you may need to take care of. Will the program behave differently in different situations? In the bread example, can you substitute something for sugar if you don’t have any? Will you get a different type of bread if you add sugar instead of salt? You should write out what the program will do in these situations.
All these specific interactions can be visualized in a flowchart. You can trace many different routes through your flowchart, each route representing a different possible implementation and outcome, like in Figure 2.
Try This: A Flowchart
You need to clean up after your baking adventure. There are two things you need to do: wash the dishes and take out the trash, in that order. Organize the following steps and decision points into a flowchart like in Figure 1. Use as many steps/decisions as possible but you do not have to use them all.
Step: Rinse dish Decision: Anything else to put in the trash bag?
Step: Sing a song Decision: Am I happy with my baking skills?
Step: Tie up trash bag Decision: Any more dirty dishes left?
Step: Take trash outside Decision: Should I watch a movie tonight?
Step: Pick up a dirty dish
Step: Scrub dirty dish with soap
Step: Put clean dish in drying rack
Step: Dump out trash bag on the floor
Step: Put a piece of trash in the trash bag
At this point, you have come up with test cases, special behaviors you might have to be careful of, and a visual representation of a sequence of steps that you believe will accomplish the task given. If you drew out your sequence of steps, now’s the time to convert your drawing into words, using programming concepts. To solve the problem you must come up with a sequence of steps to follow such that they achieve the task outlined in the problem.
Pseudocode is a mix of English and programming on paper or in your code editor. It helps you get the structure of the program looking right: when you get input from the user, when you show output, when you need to make a decision, and when you need to repeat a set of steps.
Putting the sequence of steps into words is like writing down and trying out your recipe. You must use what you know about how ingredients taste and what they’re used for to decide the best way to arrange them together. In programming, you must use everything you know about different techniques and constructs to put the code together, which is the hardest part of programming.
In pseudocode, finding the area of a circle might look like this
- Get a radius from the user
- Apply formula
- Show the result
- Repeat 1-3 until user says to stop
Certain programming concepts are useful in various ways. The only way to know when to use which concepts is through intuition, which comes with a lot of practice.
Many ways to achieve a task with programming exist. It isn’t a bad thing to go down one path and find yourself stuck; now you have a better understanding of why a particular method doesn’t work in that case. With time and experience, you’ll develop an intuition for when one concept is better to use than another.
Try This: Pseudocode
Here’s a problem statement. The Pythagorean Theorem is a2+b2=c2. Solve for c. Write a sequence of steps, in pseudocode, that you might take to solve for c. Hint: √(x2) = x
Keep value of interest on the left: c2 = a2 + b2
Take the square root: c = √( a2 + b2)
Writing Readable Code
As you learn more about Python programming, you’ll see different language specifics that Python offers to help you achieve this principle. I won’t discuss those in this lesson. What’s important to remember at this point, before you even start writing code, is that any code you write should be with the intent that someone else will read it, including yourself in a few weeks’ time!
Using Descriptive and Meaningful Names
Here’s a short snippet of code, which you don’t need to understand how to write on your own right now. It consists of three lines of code, evaluated in order, top to bottom. Notice how it looks similar to something you may write in a math class.
a = 3.1 b = 2.2 c = a*b*b
Can you tell, at a high level, what the code’s supposed to calculate? No. Suppose I rewrite the code.
pi = 3.1 radius = 2.2 # use the formula to calculate the area of a circle circle_area = pi*radius*radius
Now can you tell, at a high-level, what the code’s supposed to do? Yes! It calculates, or rather estimates, the area of a circle with radius 2.2. Like in math, Python uses variables to store data. A key idea behind writing readable code when programming is using descriptive and meaningful variable names. In the code above, pi is a variable name and you can use it to refer to the value 3.1. Similarly, radius and circle_area are variable names.
Commenting Your Code
Also notice there was a line that started with the # character. In Python, that line’s called a comment. It’s not part of code that runs when the program runs. Instead, comments are used in code to describe important parts of the code.
Definition A comment is a line in a program that starts with a #. These lines are ignored by the interpreter when running a program.
Comments should help others, and yourself, understand why you wrote code in that way. They shouldn’t only put into words what the code implements. A comment that says “use the formula to calculate the area of a circle” is much better than one that says “multiply pi times the radius times the radius.” Notice that the former explains why the code’s correct to use, but the latter puts into words what the code’s implementing. In this example, someone else reading the code already knows that you’re multiplying the three values together (because they know how to read Python code!) but they mightn’t know why you’re doing the multiplication.
Comments are useful when they describe the rationale behind a larger chunk of code, particularly when you come up with a unique way to compute or implement something. A comment should describe the big idea behind the implementation of that particular chunk of code, because it mightn’t be obvious to others. Once someone reading the code understands the big picture, they can then go into the specifics of the code by reading each line to see exactly what calculations you’re doing.
Try This: Comment the Code
Here is a short piece of Python code implementing a solution to the following problem.
“You are filling a pool and have two hoses. The green hose fills it in 1.5 hours and the blue hose fills it in 1.2 hours. You want to speed up the process by using both hoses. How long will it take using both hoses, in minutes?”
# Your comment here time_green = 1.5 time_blue = 1.2 # Your comment here minutes_green = 60*time_green minutes_blue = 60*time_blue # Your comment here rate_hose_green = 1/minutes_green rate_hose_blue = 1/minutes_blue # Your comment here rate_host_combined = rate_hose_green + rate_hose_blue # Your comment here time = 1/rate_host_combined Answer: # initialize times to fill pool (in fraction hours) # convert times to minutes # convert times to rates # add rates # solve for rate when using both hoses