abrash-black-book/10-01.md
2013-12-30 20:26:41 +11:00

125 lines
No EOL
7.8 KiB
Markdown

Chapter 10\
Patient Coding, Faster Code {#Heading1}
----------------------------
### How Working Quickly Can Bring Execution to a Crawl {#Heading2}
My grandfather does *The New York Times* crossword puzzle every Sunday.
In ink. With nary a blemish.
The relevance of which will become apparent in a trice.
What my grandfather is, is a pattern matcher *par excellence*. You're a
pattern matcher, too. So am I. We can't help it; it comes with the
territory. Try focusing on text and not reading it. Can't do it. Can you
hear the voice of someone you know and not recognize it? I can't. And
how in the Nine Billion Names of God is it that we're capable of
instantly recognizing one face out of the thousands we've seen in our
lifetimes—even years later, from a different angle and in different
light? Although we take them for granted, our pattern-matching
capabilities are surely a miracle on the order of loaves and fishes.
By "pattern matching," I mean more than just recognition, though. I mean
that we are generally able to take complex and often seemingly woefully
inadequate data, instantaneously match it in an incredibly flexible way
to our past experience, extrapolate, and reach amazing conclusions,
something that computers can scarcely do at all. Crossword puzzles are
an excellent example; given a couple of letters and a cryptic clue,
we're somehow able to come up with one out of several hundred thousand
words that we know. Try writing a program to do that! What's more, we
don't process data in the serial brute-force way that computers do.
Solutions tend to be virtually instantaneous or not at all; none of
those "N log N" or "N^2"^ execution times for us.
It goes without saying that pattern matching is good; more than that,
it's a large part of what we are, and, generally, the faster we are at
it, the better. Not always, though. Sometimes insufficient information
really is insufficient, and, in our haste to get the heady rush of
coming up with a solution, incorrect or less-than-optimal conclusions
are reached, as anyone who has ever done the *Times* Sunday crossword
will attest. Still, my grandfather does that puzzle every Sunday *in
ink*. What's his secret? Patience and discipline. He never fills a word
in until he's confirmed it in his head via intersecting words, no matter
how strong the urge may be to put something down where he can see it and
feel like he's getting somewhere.
There's a surprisingly close parallel to programming here. Programming
is certainly a sort of pattern matching in the sense I've described
above, and, as with crossword puzzles, following your programming
instincts too quickly can be a liability. For many programmers, myself
included, there's a strong urge to find a workable approach to a
particular problem and start coding it *right now*, what some people
call "hacking" a program. Going with the first thing your programming
pattern matcher comes up with can be a lot of fun; there's instant
gratification and a feeling of unbounded creativity. Personally, I've
always hungered to get results from my work as soon as possible; I
gravitated toward graphics for its instant and very visible
gratification. Over time, however, I've learned patience.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *I've come to spend an increasingly large portion of my time choosing algorithms, designing, and simply giving my mind quiet time in which to work on problems and come up with non-obvious approaches before coding; and I've found that the extra time up front more than pays for itself in both decreased coding time and superior programs.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
In this chapter, I'm going to walk you through a simple but illustrative
case history that nicely points up the wisdom of delaying gratification
when faced with programming problems, so that your mind has time to chew
on the problems from other angles. The alternative solutions you find by
doing this may seem obvious, once you've come up with them. They may not
even differ greatly from your initial solutions. Often, however, they
will be much better—and you'll never even have the chance to decide
whether they're better or not if you take the first thing that comes
into your head and run with it.
#### The Case for Delayed Gratification {#Heading3}
Once upon a time, I set out to read *Algorithms*, by Robert Sedgewick
(Addison-Wesley), which turned out to be a wonderful, stimulating, and
most useful book, one that I recommend highly. My story, however,
involves only what happened in the first 12 pages, for it was in those
pages that Sedgewick discussed Euclid's algorithm.
Euclid's algorithm (discovered by Euclid, of Euclidean geometry fame, a
very long time ago, way back when computers still used core memory) is a
straightforward algorithm that solves one of the simplest problems
imaginable: finding the greatest common integer divisor (GCD) of two
positive integers. Sedgewick points out that this is useful for reducing
a fraction to its lowest terms. I'm sure it's useful for other things,
as well, although none spring to mind. (A long time ago, I wrote an
article about optimizing a bit of code that wasn't even vaguely
time-critical, and got swamped with letters telling me so. I knew it
wasn't time-critical; it was just a good example. So for now, close your
eyes and *imagine* that finding the GCD is not only necessary but must
also be done as quickly as possible, because it's perfect for the point
I want to make here and now. Okay?)
The problem at hand, then, is simply this: Find the largest integer
value that evenly divides two arbitrary positive integers. That's all
there is to it. So warm up your pattern matchers...and go!
### The Brute-Force Syndrome {#Heading4}
I have a funny feeling that you'd already figured out how to find the
GCD before I even said "go." That's what I did when reading
*Algorithms;* before I read another word, I had to figure it out for
myself. Programmers are like that; give them a problem and their eyes
immediately glaze over as they try to solve it before you've even shut
your mouth. That sort of instant response can certainly be impressive,
but it can backfire, too, as it did in my case.
You see, I fell victim to a common programming pitfall, the
"brute-force" syndrome. The basis of this syndrome is that there are
many problems that have obvious, brute-force solutions—with one small
drawback. The drawback is that if you were to try to apply a brute-force
solution by hand—that is, work a single problem out with pencil and
paper or a calculator—it would generally require that you have the
patience and discipline to work on the problem for approximately seven
hundred years, not counting eating and sleeping, in order to get an
answer. Finding all the prime numbers less than 1,000,000 is a good
example; just divide each number up to 1,000,000 by every lesser number,
and see what's left standing. For most of the history of humankind,
people were forced to think of cleverer solutions, such as the Sieve of
Eratosthenes (we'd have been in big trouble if the ancient Greeks had
had computers), mainly because after about five minutes of brute
force-type work, people's attention gets diverted to other important
matters, such as how far a paper airplane will fly from a second-story
window.