Peering Into the Past with Cool Madison 05-09-2011
A scant few minutes ago, I was scouring my file system in a failed attempt at locating an old text file I had written some indeterminate number of months ago which compiled a list of books that I wished to procure. I subconsciously over-engineered that first sentence to be as ostensibly elegant (but honestly superfluous) as possible; it could have easily read, "Just now, I was trying to find an old wish list of books that I had compiled in a text file." My mind led me to something far more painful and absurd, probably to overcompensate for what I actually found in my search: a treasure trove of code that I wrote from my first two years of college! Much of what I found was bewilderingly awful, but I believe that I can remark that I am not seriously embarrassed. Everyone writes code (and, more generally, does things) early in their timeline that later versions of Self would not conceive of; and besides, it is legitimately fun to take a look back at old code and try to put together what was going on in the mind at the time of creation. This latter point was especially true for me when I re-discovered one of my old favorites: coolmadison.py!
Written in the Spring semester of my Sophomore year of college (most recently modified on February 23, 2009 at 6:45:52 PM, apparently), coolmadison.py has a simple premise: print an ASCII-art representation of James Madison standing in a cool pose and spouting random quotes from the Bill of Rights. That's it. I distinctly remember my chain of thoughts as the idea came to mind:
START "I have not written any Python in over a year" =>
"I would like to write some Python" =>
"I'll write something basic, something to get my toes wet" =>
"But it has to have some sort of utility, it cannot just be a dumb script that exists solely to exist" =>
"I should get back to reading up on Existentialism because that doesn't feel quite right"* =>
"Anyway, it should at least amuse me" =>
"This cowsay thing is pretty amusing, why not take a different angle on it?"** =>
"I know: I'll go historical" =>
"George Washington? No that's expected. James Madison? Still cool" =>
"What did Madison do? Oh yeah, the Bill of Rights" =>
"... Damn, I'm a genius" END
* I did not really have that thought: it was inserted at write time because I did have the previous thought but that previous one is not a thought that I would have now and I am constantly at war with past selves and you are watching it manifest. Welcome to my psyche!
** Yes, I do think in hyperlinks.
And so I sat down to write what would surely become the pre-eminent shell script of the age and emerged from a daze an hour later with something marvelous:
Perfect! I thought that this was hilarious then, and I still find it funny now. Notice how Madison gives you the wink and the gun, while holding his right hand in his pocket as if to say, "Yeah I introduced the Bill of Rights to the Constitution and ushered in an era of civilian freedom, what of it?" And check out that debonair attire, and his perfectly styled affects! Has there ever been another entity which strolled the Earth with such sophistication and elegance? I only wish I could take credit for the art, but alas I am almost positive that I took it from an ASCII-art generator somewhere (if you believe that this may have been your ASCII-art and that I am in err for using it, I apologize). What I did do, however, was to put this wonderfully handsome fellow together with random quotes from his magnum opus and unleash it upon bash prompts the world over, which is to say on my own computers and maybe another one or two machines belonging to some friends who had the misfortune of receiving this work via email.
But how did I accomplish such monumental advances, you ask? Let's take a look.
Oh... oh goodness what is this abomination? The first thing that may jump to mind is the completely unused isPunct(char) function. Supposedly, I was going to use it (maybe even for something punctuation-related?) but simply never got around to it. Then, in my Sophomoric wisdom, neglected to remove the function. Cool. But let's examine the actual meat of the program a bit closer. The basic flow of the first "half" of the program goes like this: open up the Bill of Rights text file, pick a random integer below the hard coded line count of said file and then manually iterate through each line in the file until we have reached the line indexed with our random integer and concatenate the text to an empty String. There are some obvious issues here: hard-coding the upper bound on our random generation and concatenating a String (which is all that we want!) to an empty String. The latter happened, presumably, because all that I really knew at the time was Java and was dead-set on the ideas of manual variable declaration and static typing.
But what about the way that the random line is actually accessed? Well, the idea was to iterate by line, incrementing a counter with each new line until the counter reached the random number, at which point the line was returned. It is a sluggish, ugly implementation and can be improved without a doubt. But in terms of performance, it's not actually that far off. Alternatives may have been to use something along the lines of linecache for direct access to the desired line or to just cut to the chase and call readlines() to get a list of lines which could then be directly indexed. Either way, there would have been a process of iterating through the file to read it all into the buffer. In this implementation, we save on storage in that we do not actually store the entire file, and we are at least explicit in what we are doing. For something of this miniscule size, it's not a completely atrocious methodology to retrieve the line. But: my word is it hideous! And it is by no means Pythonic. And I was certainly not carefully considering the performance trade-offs at play; rather, I simply discovered a way to make it work and stuck with it. It was a young programmer writing code that "worked" and moving on.
The second "half" of the program, however, is entirely unforgivable. In order, the program prints an empty line (for some whitespace separation in the shell), prints the quoted line, opens the file containing the ASCII Madison, prints each line one-by-one, closes the file and prints another empty line for good measure. Holy ignorance of I/O, Batman! All I can do at this point is shake my head and blame it on youth because this was a catastrophe. Why I never reached the obvious conclusion of slashing down my I/O calls, and why I thought 16 print statements for the ASCII file was a better option than reading in the file and printing it all at once, is entirely beyond me. The only thing known with certainty is that I made 20 print calls where one would have sufficed just fine. Shudder.
And, of course, this code is just aesthetically unpleasing. There's inconsistency in the comments (which are entirely unneeded here and serve only to clutter up the file), the main() call flies in the face of Pythonic convention and is both ambiguous and presumptuous about the scope in which the main program is running and the user is forced to manually call $ python coolmadison.py in the prompt because of the lack of a shebang. The code also runs right up to the end of the file without an extra line of whitespace. And did I mention the completely unused function sitting around on its parentheses? There's just so many little bits of awful that one feels a strong urge to gouge one's eyes out.
So what can we do? Why, we can improve it, of course! With the goal of producing identical output, here we go:
Ah, that's better. Clean code is happy code and this is certainly much fresher. Here we choose to read the entire Bill of Rights into a list of Strings, which allows us to avoid hard-coding our random generation range and to directly access the line that we desire for the quote. We concatenate a new line with the by line and the accessed line to form our entire quote. Of course, more efficient methods for concatenation exist in Python but for something this small the performance gains are negligible, and so we went with simplicity. To get the ASCII Madison, we make one read() call and store the entire file's contents inside of a String. We then wrap it up by printing the quote and the ASCII art all at once (-19 print calls) and calling it a hard day's work. Our reward?
The big difference from the code of February 2009 to the code of September 2011 is not performance: the program is so small as to make performance gains entirely trivial. No, the key change is in style: the code has yielded to a cleaner and sleeker form, the fat has been trimmed out, the program has become more robust and, happily, we removed about 95% of our I/O overhead. Of course, we could have made this code even sleeker by becoming less explicit, but a balance was struck. And that level of care is something which is sorely lacking from my earlier effort: I wrote a script which did what it was supposed to do and did not bother to concern myself with implementation, with style, with anything outside of the output that I could see. I don't know if my peers were writing such miserable code at age 19, but I certainly was.
And yet, there is something endearing to me, the very same perpetrator behind the crime, about the amateur effort. Young Joshua was mostly unconcerned with these finer points of programming expertise at least in part because he was just excited to create something that could make him laugh, something that he could run and be distinctly affected by. And if it made a few friends giggle or chuckle, all the better! It was all for kicks. He felt immense pleasure when the program was complete and he was able to gaze upon a winking James Madison espousing ideas about liberty. Sure, over the two and a half years since, the programmer has become at least somewhat more polished and at least a tad more knowledgeable about what he is doing. He writes better code, certainly. But it is just as important that he feels the same joy in what he does. And he, I, can say with an emphatic yes that this is the case.
I had been told by many people that as soon as you get paid to do something you love, it becomes a job. "A job." The very phrase rings with a sort of hollow desperation, a vacuous, soul-sucking connotation of have-to's. But in my time working as a "professional" engineer, this has certainly not come true. I am still feeling that teenage exultation about the code that I write. The difference is that now I do not feel such stark incredulousness toward my code. But give it six months time and I'm sure that the code I write tomorrow will look just as raw as the original Cool Madison.
Maybe by then I'll be ready for coolmadisonv3.py.
NB. If you would like to be a cool person and get down with a cool Madison, obtain all necessary bits and bytes here! Also: I found the reading list! It mentions Code Complete... my, how relevant.