This page:
| Category:
| index pages:
|
Programming Pearls
Copyright © 1986 by Bell Telephone Laboratories, Incorporated | ||||||
---|---|---|---|---|---|---|
Note (Hals): end note | ||||||
Preface | ||||||
The essays in this book are about a more glamorous aspect of the profession: programming pearls whose origins lie beyond solid engineering, in the realm of insight and creativity. Just as natural pearls grow from grains of sand that have irritated oysters, these programming pearls have grown from real problems that have irritated real programmers. The programs are fun, and they teach important programming techniques and fundamental design principles. | ||||||
Part I: Preliminaries | ||||||
Column 1: Cracking the Oyster | ||||||
1.5 Principles | ||||||
A Simple Design. Antoine de Saint-Exupéry, the French writer and aircraft designer, said that a designer knows he has arrived at perfection not when there is no longer anything to add, but when there is no longer anything to take away. More programmers should judge their work by this criterion. Simple programs are usually more reliable, secure, robust and efficient than their complex cousins, and a lot easier to build and to maintain. | ||||||
1.7 Further Reading | ||||||
In the case study described in this column, the programmers main problem was not so much technical as psychological: he couldnt make progress because he was trying to solve the wrong problem. We finally solved his problem by breaking through his conceptual block and solving an easier problem. Conceptural Blockbusting by James L. Adams [...] studies this kind of leap and is generally a pleasant prod towards more creative thinking. Although it was not written with programmers in mind, many of its lessons are particularly appropriate for programming problems. | ||||||
Column 2: Aha! Algorithms | ||||||
2.3 The Power of Primitives | ||||||
Problem B is to rotate the N-element vector X left by I positions in time proportional to N and with just a few words of extra space. | ||||||
The problem looks hard until you finally have the aha! insight: lets view the problem as transforming the array AB into the array BA, but lets also assume we have a subroutine that reverses the elements in a specified portion of the array. Starting with AB, we reverse A to get ARB, reverse B to get ARBR, and then reverse the whole thing to get (ARBR)R, which is exactly BA. This results in the following code for rotation; the comments show the results when ABCDEFGH is rotated left three elements.
| ||||||
The reversal code is time- and space-efficient, and is so short and simple that its pretty hard to get wrong. [...] Ken Thompson wrote the editor and the reversal code in 1971, and claims that it was part of the folklore even then. | ||||||
2.4 Getting It Together: Sorting | ||||||
Given a dictionary of English words (one word per input line in lower case letters), we must find all anagram classes. There are several good reasons for studying this problem. The first is technical: the solution is a nice combination of getting the right viewpoint and then using the right tools. The second reason is more compelling: wouldnt you hate to be the only person at a party who didnt know that deposit, dopiest, posited and topside are anagrams? [...] There are a number of surprisingly ineffective and complicated ways to solve this problem. Any method that considers all permutations of letters for a word is doomed to failure. The word microphotographic (an anagram of photomicrographic) has 17! permutations, and a few multiplications showed that 17! ≈ 3×1014. Even assuming the blazing speed of one microsecond per permutation, this will take 3×108 seconds. The rule of thumb that π seconds is a nanocentury is true to within half a percent (there are roughly 3.155×109 seconds in a century), so the 3×108 seconds are almost a decade. Note (Hals): end note | ||||||
2.5 Principles | ||||||
A Problem Solvers Perspective. Good programmers are a little bit lazy: they sit back and wait for an insight rather than rushing forward with their first idea. That must, of course, be balanced with the initiative to code at the proper time. The real skill, though, is knowing the proper time. That judgment comes only with the experience of solving problems and reflecting on their solutions. | ||||||
Column 3: Data Structures Programs | ||||||
Most programmers have seen them, and most good programmers realize theyve written at least one. They are huge, messy, ugly programs that should have been short, clean, beautiful programs. I once saw a COBOL program whose guts were
[...] Think for a minute about how you could accomplish the same task with a program just a tiny fraction of the size by using a different data structure a 500-element array to replace the 500 individual variables. (Programmers who are paid per line of code may wish to ignore this exercise; managers who pay by the line may wish to faint.) Hence the title of this column: a proper view of data does indeed structure programs. | ||||||
3.5 Principles | ||||||
The moral of each of the stories is the same: dont write a big program when a little one will do. Most of the structures exemplify what Polya calls the Inventors Paradox in his How To Solve It: the more general problem may be easier to solve. In programming this means that it may be harder to solve a 73-case problem directly than to write a general program to handle the N-case version, and then apply it to the case that N=73. This column has concentrated on just one contribution that data structures can make to software: reducing big programs to small programs. Data structure design can have many other positive impacts, including time and space reduction and increased portability and maintainability. Fred Brookss comment in Chapter 9 of his Mythical Man Month is stated for space reduction, but it is good advice for programmers who desire the other attributes as well [...] | ||||||
Here are a few principles to ponder as you rear back. Rework repeated code into arrays. A long stretch of similar code is often best expressed by the simplest of data structures, the array. Be familiar with advanced data structures. Advanced data structures arent appropriate very often, but when you need them, theyre indispensable. Let the data structure the program. The theme of this column is that data can structure a program by replacing complicated code by a data structure. There are many other interpretations of the advice. [...] Although the particulars change, the theme remains: before writing code, good programmers thoroughly understand the input, the output, and the intermediate data structures around which their programs are built. | ||||||
Column 4: Writing Correct Programs | ||||||
Coding skill is just one small part of writing correct programs. The majority of the task is the subject of the three previous columns: problem definition, algorithm design, and data structure selection. If you perform those tasks well, writing correct code is usually easy. | ||||||
4.1 The Challenge of Binary Search | ||||||
I was amazed: given ample time, only about ten percent of professional programmers were able to get this small program right. But they arent the only ones to find this task difficult: in the history in Section 6.2.1 of his Sorting and Searching, Knuth points out that while the first binary search was published in 1946, the first published binary search without bugs did not appear until 1962. | Topic: | |||||
Part II: Performance | ||||||
Column 5: Perspective on Performance | ||||||
5.3 Principles | ||||||
Because an ounce of prevention is worth a pound of cure, we should keep in mind an observation due to Gordon Bell of Encore Computer Corporation. The cheapest, fastest and most reliable components of a computer system are those that arent there. Those missing components are also the most accurate (they never make mistakes), the most secure (they cant be broken into), and the easiest to design, document, test and maintain. The importance of a simple design cant be overemphasized. But when performance problems cant be sidestepped, thinking about design levels can help focus a programmers effort. | ||||||
Column 6: The Back of the Envelope | ||||||
6.2 Quick Calculations in Computing | ||||||
Early in the life of a system, rapid calculations can steer the designer away from dangerous waters into safe passages. And if you dont use them early, they may show in retrospect that a project was doomed to failure. The calculations are often trivial, employing no more than high school mathematics. The hard part is remembering to use them soon enough. | ||||||
6.5 Principles | ||||||
When you use back-of-the-envelope calculations, be sure to recall Einsteins famous advice. Everything should be made as simple as possible, but no simpler. We know that simple calculations arent too simple by including safety factors to compensate for our mistakes in estimating parameters and our ignorance of the problem at hand. | Topic: | |||||
6.8 Quick Calculations in Everyday Life [Sidebar] | ||||||
Everyday life presents us with many opportunities to hone our skills at quick calculations. For instance, how much money have you spent in the past year eating in restaurants? I was once horrified to hear a New Yorker quickly compute that he and his wife spend more money each month on taxicabs than they spend on rent. And for California readers (who may not know what a taxicab is), how long does it take to fill a swimming pool with a garden hose? | ||||||
Several other readers discussed teaching this attitude to children, from the viewpoints of both parent and child. Popular questions were of the form How long would it take you to walk to Washington, D.C.? and How many leaves did we rake this year? Administered properly, such questions seem to encourage a life-long inquisitiveness in children, at the cost of bugging the heck out of the poor kids at the time. | Topic: | |||||
Column 8: Code Tuning | ||||||
Some programmers pay too much attention to efficiency; by worrying too soon about little optimizations they create ruthlessly clever programs that are insidiously difficult to maintain. Others pay too little attention; they end up with beautifully structured programs that are utterly inefficient and therefore useless. Good programmers keep perspective on efficiency: it is just one of many problems in software, but it is sometimes very important. [...] Code tuning locates the expensive parts of an existing program and then makes little changes to the code to improve its performance. Its not always the right approach to follow and its rarely glamorous, but it can sometimes make a big difference in a programs performance. | ||||||
8.4 Principles | ||||||
The most important principle about code tuning is that it should be done rarely. | ||||||
The Role of Efficiency. Many other properties of software are as important as efficiency, if not more so. Don Knuth has observed that premature optimization is the root of much programming evil; it can compromise the correctness, functionality and maintainability of programs. Save concerns for efficiency for when it matters. | ||||||
Design Levels. [...] Before tuning code, we should make sure that other approaches dont provide a more effective solution. | ||||||
Column 9: Squeezing Space | ||||||
9.3 Code Space | ||||||
Although one cant ignore the many merits of top-down methods, the homogeneous world view given by good primitive routines can make a system easier to maintain and simultaneously reduce space. Featurecide reduces code space by removing functionality from subroutines. | ||||||
Part III: The Product | ||||||
Column 11: Searching | ||||||
11.3 The Design Space | ||||||
One part of a programmers job is solving todays problem. Another, and perhaps more important, part of the job is to prepare for solving tomorrows problems. Sometimes that preparation involves taking classes or studying books like Knuths. More often, though, we programmers learn by the simple mental exercise of asking how we might have solved a problem differently. | ||||||
One student suggested photocopying the precinct list, cutting the copy with a paper slicer, shaking the slips in a paper bag, and then pulling out the required number of slips. That cadet showed the conceptual blockbusting that is the subject of Adamss book cited in Section 1.7. Page 57 of that book sketches Arthur Koestlers views on three kinds of creativity. Ah! insights are his name for originality, and aha! insights are acts of discovery. He would call this cadets solution a haha! insight: the low-tech answer to a high-tech question is an act of comic inspiration [...] | book citation | |||||
11.4 Principles | ||||||
Although the following discussion presents the stages in one natural order, the design process is more active: we hop from one activity to another, usually iterating through each many times before arriving at an acceptable solution. Understand the Perceived Problem. Talk with the user about the context in which the problem arises. Problem statements often include ideas about solutions: like all early ideas, they should be considered but not followed slavishly. Specify an Abstract Problem. A clean, crisp problem statement helps us first to solve this problem and then to see how this solution can be applied to other problems. Explore the Design Space. Too many programmers jump too quickly to the solution to their problem; they think for a minute and code for a day rather than thinking for an hour and coding for an hour. | ||||||
Implement One Solution. On lucky days our exploration of the design space shows that one program is far superior to the rest; at other times we have to prototype the top few to choose the best. We should strive to implement the chosen design in straightforward and succinct code. Problem 5 describes a class exercise that I graded on programming style. Most students turned in one-page solutions and received mediocre grades. Two students who had spent the previous summer on a large software development project turned in beautifully documented five-page programs, broken into a dozen procedures, each with an elaborate heading. They received failing grades. My program worked in five lines of code, and their inflation factor of sixty was too much for a passing grade. When they complained that they were employing standard software engineering tools, I should have quoted Pamela Zave: The purpose of software engineering is to control complexity, not to create it. | ||||||
Column 12: Heaps | ||||||
12.3 Priority Queues | ||||||
There are two sides to any data structure. Looking from the outside, its abstraction tells what it does a queue maintains a sequence of elements under the operations of insert and extract. On the inside, its implementation tells how it does it a queue might use an array or a linked list. | ||||||
12.5 Principles | ||||||
Abstraction. Good engineers distinguish between what a component does (the abstraction seen by the user) and how it does it (the implementation inside the black box). In this column weve seen two ways to package black boxes: procedural abstraction and abstract data types. | ||||||
Column 13: A Spelling Checker | ||||||
13.2 The Design Space | ||||||
An affix is a prefix like pre- or a suffix like -ly; most dictionaries leave affix analysis to the reader. Although a spelling checker may in good conscience report antidisestablishmentarianism as a mistake, I would be miffed to wade through a long list of misspellings like cats and replay when cat and play were in the dictionary. | ||||||
Epilog | ||||||
[...] thinking hard about programming can be both useful and fun. Theres more to the job than systematic program development from formal requirements documents. If this book helps just one disillusioned programmer to fall back in love with his or her work, it will have served its purpose. | ||||||
Here are some points that occur repeatedly in Principles sections. Work on the right problem. The points were originally discussed in the context of programming, but they apply to any engineering endeavor. | ||||||
text checked (see note) Feb 2024 |