$$\newcommand{\tp}{\thinspace .}$$

Much of the material in this document is taken from Appendix F in the book A Primer on Scientific Programming with Python, 4th edition, by the same author, published by Springer, 2014.

# A recipe for program writing and debugging

1. Understand the problem. Make sure that you really understand the task the program is supposed to solve. We can make a general claim: if you do not understand the problem and the solution method, you will never be able to make a correct program. It may be argued that this claim is not entirely true: sometimes students with limited understanding of the problem are able to grab a similar program and guess at a few modifications, and actually get a program that works. But this technique is based on luck and not on understanding. The famous Norwegian computer scientist Kristen Nygaard (1926-2002) phrased it precisely: Programming is understanding. It may be necessary to read a problem description or exercise many times and study relevant background material before starting on the programming part of the problem solving process.

2. Work out examples. Start with sketching one or more examples on input and output of the program. Such examples are important for controlling the understanding of the purpose of the program, and for verifying the implementation.

3. Decide on a user interface. Find out how you want to get data into the program. You may want to grab data from the command-line, a file, or a dialog with questions and answers.

4. Make algorithms. Identify the key tasks to be done in the program and sketch rough algorithms for these. Some programmers prefer to do this on a piece of paper, others prefer to start directly in Python and write Python-like code with comments to sketch the program (this is easily developed into real Python code later).

5. Look up information. Few programmers can write the whole program without consulting manuals, books, and the Internet. You need to know and understand the basic constructs in a language and some fundamental problem solving techniques, but technical details can be looked up.

The more program examples you have studied, the easier it is to adapt ideas from an existing example to solve a new problem.

6. Write the program. Be extremely careful with what you write. In particular, compare all mathematical statements and algorithms with the original mathematical expressions.

In longer programs, do not wait until the program is complete before you start testing it, test parts while you write.

7. Run the program. If the program aborts with an error message from Python, these messages are fortunately quite precise and helpful. First, locate the line number where the error occurs and read the statement, then carefully read the error message. The most common errors (exceptions) are listed below.

SyntaxError: Illegal Python code.

  File "somefile.py", line 5
x = . 5
^
SyntaxError: invalid syntax


Often the error is precisely indicated, as above, but sometimes you have to search for the error on the previous line.

NameError: A name (variable, function, module) is not defined.

  File "somefile.py", line 20, in <module>
table(10)
File "somefile.py", line 16, in table
value, next, error = L(x, n)
File "somefile.py", line 8, in L
exact_error = log(1+x) - value_of_sum
NameError: global name 'value_of_sum' is not defined


Look at the last of the lines starting with File to see where in the program the error occurs. The most common reasons for a NameError are

• a misspelled name,
• a variable that is not initialized,
• a function that you have forgotten to define,
• a module that is not imported.
TypeError: An object of wrong type is used in an operation.

  File "somefile.py", line 17, in table
value, next, error = L(x, n)
File "somefile.py", line 7, in L
first_neglected_term = (1.0/(n+1))*(x/(1.0+x))**(n+1)
TypeError: unsupported operand type(s) for +: 'float' and 'str'


Print out objects and their types (here: print x, type(x), n, type(n)), and you will most likely get a surprise. The reason for a TypeError is often far away from the line where the TypeError occurs.

ValueError: An object has an illegal value.

  File "somefile.py", line 8, in L
y = sqrt(x)
ValueError: math domain error


Print out the value of objects that can be involved in the error (here: print x).

IndexError: An index in a list, tuple, string, or array is too large.

  File "somefile.py", line 21
n = sys.argv[i+1]
IndexError: list index out of range


Print out the length of the list, and the index if it involves a variable (here: print len(sys.argv), i).

8. Verify the results. Assume now that we have a program that runs without error messages from Python. Before judging the results of the program, set precisely up a test case where you know the exact solution. This is in general quite difficult. In complicated mathematical problems it is an art to construct good test problems and procedures for providing evidence that the program works.

If your program produces wrong answers, start to examine intermediate results. Never forget that your own hand calculations that you use to test the program may be wrong!

9. Use a debugger. If you end up inserting a lot of print statements in the program for checking intermediate results, you might benefit from using a debugger as explained in the section Using a debugger.

Some may think that this list of nine points is very comprehensive. However, the recipe just contains the steps that you should always carry out when developing programs. Never forget that computer programming is a difficult task.

Program writing is substantially more demanding than book writing. Why is it so? I think the main reason is that a larger attention span is needed when working on a large computer program than when doing other intellectual tasks. Donald Knuth [1] (p. 18), computer scientist, 1938-.