CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Style
Some Style Facts:
- Good style is...
- Both an art and a science.
- Open to some debate, yet mostly agreed upon.
- The product of years of suffering due to bad style.
- Good style reduces time spent debugging, and...
- Good style reduces errors, and...
- Some Style Guides (which are not OUR style guide):
15-112 Style Rubric
- Clarity Rules
You must include your name, andrewId, and section in a comment at the top of every file you submit.
2-point error: not writing your name/andrewId/section in a submitted file
You should write concise, clear, and informative comments that make your code even easier to understand.
- Comments should be included with any piece of code that is not self-documenting.
- Comments should also be included at the start of every function (including helper functions).
- Comments should not be written where they are not needed.
2-point error: writing too many or too few comments, or writing bad comments.
Here is an example comment for a palindrome function from the string notes:
# return True if the characters are identical at forward and reversed indices def isPalindrome3(s): for i in range(len(s)): if (s[i] != s[-1-i]): return False return True
- Helper Functions (Top-Down Design)
You should use top-down design to break down large programs into helper functions where appropriate.
- This also means that no function should become too long (and therefore unclear).
2-point error: using too many or too few helper functions.
2-point error: writing a function that is more than 30 lines long.
- Exceptions: blank lines and comments do not count towards this line limit.
- Variable Names
Use meaningful variable and function names (whenever possible).
- Variables and functions should be written in the camelCase format. In this format, the first letter is in lowercase, and all following words are uppercased (eg: tetrisPiece).
- For example, some good, meaningful variable names for a graphics problem with a flag might include numStars, numStripes, stripeColor, or stripeWidth.
- On the other hand, it can be hard to know what variables named a, b, and c do, especially if you haven't written any comments either!
- Short variable names are ok if it is relatively obvious what they do. For example, cx and cy are commonly use for the center point of a circle, and r is probably the circle's radius.
- Variable names should not overwrite built-in function names; for example, str is a bad name for a string variable.
- Common built-in keywords to avoid include dict, dir, id, input, int, len, list, map, max, min, next, object, set, str, sum, and type.
2-point error: using some non-meaningful variable names.
- Exceptions: i/j for index/iterator, c for character, s for string, and n/x/y for number.
2-point error: using a built-in function name as a variable.
- Unused Code
Your code should not include any dead code (code that will never be executed).
- Additionally, all debugging code should be removed once your program is complete, even if it has been commented out.
Your code formatting should make your code readable. This includes:
- Not exceeding 80 characters in any one line (including comments!).
- Indenting consistently. Use spaces, not tabs, with 4 spaces per indent level (most editors let you map tabs to spaces automatically).
- Using consistent whitespace throughout your code.
- Good whitespace: x=y+2, x = y+2, or x = y + 2
- Bad whitespace: x= y+2, x = y +2, or x = y + 2
- Robustness Rules
- Test Functions
You should always include test functions for each function that is reasonably testable.
- At the beginning of the semester we provide some test functions in the homework starter files, but we will phase these out over time. You should start supplementing our tests with your own as of hw3.
2-point error: not writing enough test functions.
- Exceptions: you do not need to test interactive, random, graphics, data initialization, or event functions.
In general, your code should be reasonably efficient.
- As this is an introductory course, we do not expect you to write the most efficient solutions generally. However, your code may not be "grossly" inefficient, especially if you had simple, clear, and reasonably efficient options to choose from.
2-point error: writing a function that works correctly but takes more than 5 seconds to test (with some exceptions).
- Repetitive Code
In general, you should not have repetitive code.
- One example of this is duplicate code, code that has been copy-pasted. This should instead be put into a helper function.
- Another example is "numbered"-code, where there are multiple variables like foo0, foo1, foo2. These should be represented by a loop or list instead.
2-point error: having 3 or more instances of repeated code.
- Magic Numbers
Your code should not include magic numbers.
- A magic number is a number used outside of a variable assignment in code that does not have an immediate and clear meaning.
- In general, every number besides -1, 0, 0.5, 1, 2, and 10 is magic.
- If you must use a magic number, store the number or an expression using the number in a variable, then use that variable.
Example: Here's code to draw a grid of clocks, similar to what you saw in the graphics notes. For numbers like 80 (the width and height of the clocks) note that we're storing them in variables so that we can change the behavior of our code easily.
# Draw a whole grid of clocks! (Without magic numbers!) def drawClockGrid(canvas, width, height): clockWidth = 80 clockHeight = 80 numRows=3 numCols=4 margin = 5 hour = 0 minute = 0 incrementHour = 13 for row in range(numRows): for col in range(numCols): left = col * clockWidth + margin top = row * clockHeight + margin right = left + clockWidth - margin bottom = top + clockHeight - margin hour += incrementHour drawClock(canvas, left, top, right, bottom, hour, minute)
Now, this is the same code, but written using magic numbers. This code is hard to understand, changing this code's behavior is difficult, and it's very easy to break. Don't do this!# Magic numbers! Don't do this! def drawClockGrid(canvas, width, height): hour = 0 for row in range(3): for col in range(4): left = col * 80 + 5 top = row * 80 + 5 right = col * 80 + 80 bottom = row * 80 + 80 hour += 13 drawClock(canvas, left, top, right, bottom, hour, 0)
This is even worse!# DEFINITELY don't do this! def drawClockGrid(canvas, width, height): hour = 0 for row in range(3): for col in range(4): hour += 13 drawClock(canvas, col*80+5, row*80+5, col*80+80, row*80+80, hour, 0)
- If/Else Statements
When reasonable, multiple if statements should be joined into an if-elif-else chain. For example, the following code:if (x < 2): foo() if (x > 10): bar() if (2 <= x <= 10): oh()should beif (x < 2): foo() elif (x > 10): bar() else: oh()2-point error: not joining-up multiple ifs where appropriate.
- Global Variables
Global variables can be useful, but they can also be dangerous, and often lead to complicated bugs.
2-point error: using any global variables.
- Test Functions