Helper Functions and Planning Tasks
Copyright (c) 2017 Kathi Fisler
1 When is Code "Clean"?
Generally speaking, code is clean when the structure of the code reflects the high-level process/computation that the code performs. Look at the body of total-cost from our pens example – the expression in the body summarizes the computation.
fun total-cost(num-pens :: Number, slogan :: String) -> Number: |
doc: "produce total cost of pen order including shipping" |
add-shipping(pen-cost(num-pens, slogan)) |
end |
We can read off the code to determine that the program computes the pen cost then adds the shipping. If you want the details of those steps, you can look at the bodies of the individual functions.
Programming Tip: When you get a programming problem, step back and ask what tasks or steps the computation requires. Plan to write a helper function for each of these tasks.
2 Starting Programs and Identifying Tasks
Let’s try this again on another problem. Here’s the problem statement:
Write a program that generates images of award certificates. Each certificate includes text saying which place the person achieved (like 1st, 3rd, ...), the person’s name, and a picture. For the picture, first place gets a gold star, second place gets a silver star, and all other places get some default picture that you can pick.
How would you get started on such a problem?
Note that the problem statement isn’t entirely clear on what the produced certificates should look like. Thus your first step is to determine (by talking to the person who asked for the program!) what output to produce. You decide to create images with a picture on the left and the text on the right, as follows:
Next, figure out what the main steps are to creating the certificate. These are called the tasks. Tasks are steps that should make sense to humans who may need to read your code later. For this problem, there appear to be two tasks: (1) getting the picture (that will appear on the left) and (2) getting the text that should appear on the right. The overall program will then put these pieces together.
This task analysis might lead you to sketch out the following outline of your program:
fun make-certificate(name :: String, rank :: Number) -> Image: |
doc: "create certificate with picture on left and name/place on right" |
frame( |
beside(make-picture(rank), |
make-text(name, rank))) |
end |
Notice that this code shows the structure of the image, and the structure of the overall computation. You can now proceed to write the make-picture and make-text functions to create the final image.
You can view the lecture capture to see how we got to this point, and look at the final code file to see the finished version (though we strongly suggest that you try developing these functions first as practice). Pay particular attention to how the functions and named definitions capture the structure of the computation.
3 Big Picture Take Aways
We started this course looking at how programs capture the structure of a computation or piece of complex data (i.e., images). Which expressions we use and how we nest them reflects the structure of the data or computation in a way that people can read off that structure.
Helper functions are the next step up in reflecting structure in code. We use helpers to name key tasks within our problem. Accounting for shipping costs and discounts are core steps in letting someone order items. We created a function for each task, tested each task separately from the others, then assembled the functions into our overall computation.
Key idea in CS: Use helpers to reflect the essential structure of the process you are performing in code. This aids readability of code and finding errors, since you can test each step separately.