18 August 2016

Study strategy for programming books

What is the best way to study a programming book?

In order to improve my programming skills, I am reading and reviewing some programming books and am trying to find a good method of working through the material. So far I think I am settling on a multi-pass approach, where each pass or "phase" emphasizes a different activity.


1. Read

In this phase I read the book, paying particular attention to new terms and the coding examples. If there is something unclear, I try it out in my editor. For example, say I am learning C and I am unsure what the format specifier %p actually looks like when it is printed out. I might answer this by trying out a small example program like this:

int main()
{
    int a = 1234;
    int *pa = &a;
    printf("pa: %p\n", pa);
}

As I'm reading, I highlight any important key terms. These are words that are important to the domain. For example, if I am studying Python, the words list and __construct are key terms. If I am studying C++, virtual, member function and resource acquisition is initialization are key terms. I think it is best to highlight these close to where they are first explained in the book. If highlighting is not convenient or possible, I will make an index by page number:

95: virtual
100: polymorphism
130: RAII (resource acquisition is initialization)
...

In the reading phase I also take note of relevant examples from the book. Different authors may have different terminology for this, but when I use the word example I am referring specifically to a problem which is presented in the book in a more or less solved form. So basically this refers to code listings, algorithms, "try this out" sections, etc. For such examples, it is not sufficient merely to highlight them, because that will not give a good overview later. Instead, I like to create a separate list for them, indexed by page number (this makes later review much easier). For example:

87: quicksort: Implementation of the quicksort algorithm.
100: rpncalc: Reverse Polish Notation calculator.
111: convertday: Convert year, month and day to day number. e.g. 2004,3,1 is day 61.
173: simplecp: Use WRITE system call to make a simple cp program.
...


I separate each field by ":" to make it easy to type. The middle field (e.g. "rpncalc") stands for a suggested file or folder name to refer to that example. The description should be a short reminder of what that example was about.

If it is an exercise covered in the book, I may abbreviate the description rather than rehash it:

45: detab: (Exercise 1-20).
45: entab: (Exercise 1-21) Opposite of detab.
...

Exercises are unsolved problems related to the book material. Some books include specific exercise sections which describe problems to be solved. Occasionally authors leave some exercises buried in an explanation, using a phrase such as "this part is left as an exercise to the reader". Other books, however, don't suggest any exercises. If that's the case, I make my own list of exercises by trying to go beyond the existing material, perhaps using one of the examples as a starting point. For example:

100: calc: Using rpncalc as a starting point, create a program which evaluates arbitrary mathematical expressions in normal (non-RPN) form, e.g. 5 * 7 + (1/12). The expressions must support +, -, *, / and ( ) (parentheses). To solve this problem, read about the Shunting Yard algorithm and use that in order to build on the existing rpncalc solution.

2. Implement examples

In this phase I type out the examples recorded from phase 1 and ensure that I can get them to actually work. In principle, examples taken from code listings in a book should be usable with no effort. In practice, however, some effort is always required to get an example from a book to work "for real", especially if the listing is divided into several parts on different pages. Code listings sometimes have bugs or outdated information as well, issues which must be addressed before I can get them to work. In this phase it is sufficient to merely get the examples to work; it is not necessary to understand every aspect of them yet. In fact, because I prefer to type out the listings myself, if I have any questions about any part of the example, it is quite a simple matter to just add an inline comment with that question for later review:

#include <iostream>

int main() { 
    std::ios::sync_with_stdio(false);  // --Q: Author says this improves 
                                       // performance. Does it really?
    std::cout << "Hello, world!\n";
}

If the example is a simple one, seeing how it works is usually not a problem. For more intricate examples, I often find that typing it in and actually trying to get it to work "on my own" quite often clears up any mysteries.

3. Review vocabulary

In this phase I review terms, keywords, etc. that I highlighted in phase 1. I usually find it helpful to arrange them all into a simple alphabetical list. This phase is especially helpful for studying a new area or unfamiliar topic, or if the book includes lots of key terms. If the book I'm reading is for review, this phase is quite possibly unnecessary and can probably be safely skipped.

To make my vocabulary list, I use a similar format as the examples and exercises list. For example, I recently read a book about ARM processors and made a list that looks similar to the following. The "|" symbol in the term denotes synonyms (e.g. ; and @ are synonyms in this case):

!: 69: Notation used to activate auto-indexing. Example:
    LDR r0, [r1,#4]!
#: 52: Makes an immediate value. Example:
    ADD r3, r3, #1  ; r3 := r3 + 1
; (semicolon) | @: 50: Comment introducer. Some assemblers use @ for this instead of semicolon. Example:
    ADD r3, r3, #1  @ r3 := r3 + 1
...
ADC: 51: add with carry
ADD: 51: add
ADR: 57; 69: address-to-register pseudo instruction. Example:
    ADR r1, TABLE1 ; Place the address of TABLE1 into r1.
AHB: 216: Advanced High-performance Bus
ASB: 217: Advanced System Bus
...
Cin:  13: carry in
Cout: 13: carry out. See also: Cin.
...
latency: 76
LDMFD: 61: Load Multiple Full Descending. Example:
   LDMFD r13, {r2-r9}  ; Restore registers from the (full descending) stack.
...

If I forget a term, I can refer to that list and usually remember what it was about. And I have a page number reference for more information if needed. But even if I never consult the list, I find that preparing such a list helps give me a better overview of the key topics that book had to offer.

4. Solve exercises

In the final phase, I use the exercises list that I compiled in phase 1, and begin solving them. This phase may require a lot of time, especially if the exercises are challenging. If an exercise is too hard, I first attempt to solve it but then skip it to avoid getting "stuck" on a problem. If I need help with an exercise, I can also use on-line resources such as Github (sometimes people post their solutions to book exercises here), forums, IRC channels, Stack Overflow, etc.




No comments:

Post a Comment