Some general notes about programming:
- Considerations about program efficiency (common mistakes to avoid)
- Limited importance. Do not worry about program efficiency until you are absolutely certain that it is absolutely necessary. Today's computers are amazingly powerful
machines. They can execute over a billion commands per second, so don't worry about having them do 10,000 times as much as they need to do, as long as it
still takes less than a millisecond. In general, think about how much time it takes the computer to execute a particular piece of code, multiplied by the
number of times that that piece of code will get executed (which may be many, as a result of loops, or many program runs, or many people running the
program). If the product of those two, i.e. the total time that will be spent executing a piece of code, is small, don't worry about it. If you could save
10 minutes of total execution time, by putting in 15 minutes of work, it's probably not worth it. The bottleneck for computers, nowadays, is not the
machines themselves. It's the people.
- When and what to optimize. If you are indeed absolutely certain that some piece of code will need efficiency optimization (either to reduce run time or to reduce memory usage),
don't do it in your first implementation. Write your program without considering efficiency. Then, when you're running your program, and you find it's too
slow, find out what piece of your program is too slow (it's probably less than 1% of all the code that you've written), and optimize only that piece.
- Limitations of Big O. Big O analysis of algorithm run time can be very useful. However, keep in mind that it
does not tell the full story. An algorithm with running time exponential in n can be a perfectly usable algorithm, as long as n doesn't get
too big. The constant cost, or other lower order costs, which are ignored in big O notation, could well be the dominant factor in practice. Profilers often tell you more relevant information than you can get from theoretical
analysis.
- Easy languages are not an obstacle. Don't shy away from using an easy-to-program language, such as Lisp, Python, or for some people, Matlab, just because you heard some bad
things about their execution efficiency. These languages are designed, maintained, and used by very practical and professional people, and often have a feature that allows you
to incorporate small pieces of efficiency-optimized C-code. I know that Python and Matlab allow that, and I'm sure they're not the only
ones. Choosing a hard-to-program but more execution-efficient language, over an easy-to-program but less execution-efficient language, because of considerations about the
time-critical 1% of your code, is to be avoided if at all possible.
- What is important when writing your program
- Focus on programmer and user. Do pay attention to other things than execution efficiency. As I said, the programmer and the user, i.e. the humans involved, are the bottleneck, so make your
program easy to program and easy to use. There are many methods for doing so, some better than others, and I do not know the ultimate truth about what is
the best way to do this. I can only mention my own approach at this time: I make sure that I write good functions (also known as procedures). Good
functions are functions that have clear return values, that take few parameters, and that are not long in themselves (1 to 5 lines is good; 6 to 10 lines
is probably ok; 11 to 20 lines is rather big; and more than 20 is probably going to cause you serious trouble). Using classes, where appropriate, can also
help a lot. Given the right functions and classes, many things become easy.
- Keep use of variables to a minimum. This is emphasized often by the functional programming crowd, and they're right about it. The less
state your program has, the easier it is to understand what's going on. Global variables are to be kept to an absolute minimum (although they can
be useful, at times). Local variables are less problematic, because they only complicate one function. Nevertheless, keep those, too, to a minimum.
- Constant variables are fine. One exception must be noted, both for global and for local variables. A variable that never changes its value isn't really variable -
it doesn't vary. Global variables that never change their value are called constants, and they don't really make your program less
transparent. Local variables that never change their value aren't called constants, but I think they do deserve a special name (in Scheme I think they're
called "let", as in the Mathematical phrase "let x be ..."). Anyway, those aren't really a problem, either. However, anything that does change state is
all too likely to become a liability - although, again, in some situations they are very valuable, or even necessary.
- Same for class data. What I mentioned about variables applies equally to data fields of classes. If they never change: no problem. If they do: I know that this possibility
is essential and necessary, but keep it to a minimum. In particular, do not include data fields of which the value can be inferred directly from the value
of other data fields. Instead, include a member function that does this inferring. Member functions don't add much complexity. Too many member data
fields can cause you to lose overview of what's going on, and can lead to internally contradictory class instances.
- Good languages. There are many programming languages, and each of them has a significant team of fully devoted fans and
followers. The reason for that is that every language has its qualities. I am not a fan of any particular language, but at this time, Python is my
preferred language. It is easy to program, you get clear error messages when you screw up (which happens all the time, in my case), and it's got
libraries for almost anything you can think of. On top of that, it's popular and thus well-maintained. And for those programmers seeking some of the
concepts that not every language offers nowadays: you can be sure that Python has pretty much all of them. Lists, lambda/map/reduce and list
comprehension, iterators, classes, dictionaries, an eval() function, dynamic typing, saving arbitrary data structures to disk, a good debugger, and
the list goes on. You can even create classes of which the instances are callable, like functions are. Using that feature, you can make pretty much
any feature that you feel is missing.
Back