learning common lisp through sheer stupidity
I have no idea how to program in Common Lisp, so I decided to learn for a number of reasons, the main of which being the potential to expand my methodology in respect to writing effective computer programs. And let me just say, this has not been an easy process.
I started with the basics: looking up simple youtube videos and StackOverFlow threads on what the most efficient means of learning Lisp would be. Simple enough, right? WRONG! I couldn’t figure out how to get the Steel Bank Common Lisp compiler to work correctly (alongside additional tools like QuickLisp), and after slightly more than an hour of attempting to fix my installation and wrap my head around how SBCL actually works, I eventually just gave up and installed GNU CLISP, which, FYI, worked like a complete charm.
From there, I just did the standard “Hello, World!” program.
And it seemed simple enough, but my peril with the language would only go downhill from this point onwards, as simply playing around with the absolute core basics without attempting to do any hacking would do me absolutely no good. Therefore, I immediately opted to try learning to write in Lisp via solving the easiest possible problem out of a year of Advent of Code.
Hacking Advent of Code in Lisp
Part 1: Planning
I started by picking the Day 1 problem from last year, a problem that I’ve solved in as little as four lines in Java, so I was certain that there was no way it could be too overly complicated in Lisp. I was terribly, terribly wrong.
I first had to begin by deciding on the structure of my program, and I opted to go for the kind of strictly-scoped layout that I tend to use for languages like Python or Javascript that allow for code to be run outside of functions in the global scope, which looks somewhat like the following:
Note that if this were a language that didn’t allow code at the global scope, such as C or Java, then I would simply make all of the root code from read argument
downward my main
method/function.
I wasn’t exactly certain how I would begin going about trying to solve this problem in Lisp, so I thought first about how I would try solving this problem in a language like C.
Part 2: Failure
From there, I started on the main function of my Lisp program:
At this point, I looked like I was steadily on track to solving the problem effecitvely, correct? Wrong. I kept running into prolems when attempting to develop the strip_line_to_number
function by unnecessarily overcomplicating the program.
The reason why this function layout sucks so badly is that unlike the intended imperative solution, this functional solution has to obtain the first and last characters separately as strings and then concatenate them before parsing them to a string rather than converting each character to an integer immediately then doing a simple math operation to make the final number, and the reason why this is so counterproductive can be seen in the next set of functions:
This function chooses to first manually remove all non-numeric characters from the string (which is already one \(O(n)\) loop right there), before obtaining the first and last characters of the new integer-only string as a list trhoguh indexing, and this method sucks because edge cases where there is only one number in the string have to have additional code written to manually copy the value to both our first-number
and second-number
variables.
View the final source code here.
Part 3: Success (kind of), and What I Learned About Lisp In The Process
It’s been a few weeks since I first started writing this post, and I’ll level with you: there really hasn’t been much success. At least, strictly speaking, there hasn’t been much success in solving this specific Advent of Code problem thus far, but in all honesty, I’m beginning to love Common Lisp, and I have a few reasons why.
First and foremost, I’m starting to love the switch in human-computer interaction between imperative programming and functional/declarative programming. Fundamentally, the main difference between imperative programming and functional programming is the switch between essentially describing the exact processes the computer must undergo to solve a problem (which opens up issues of side effects due to the programmer having to constantly consider changes in states of varying scopes) and alternatively, describing the problem itself and then letting the computer do the rest automatically. When computer programs are restricted to have no mutable global states with little to no typing, the programs being written reflect something closer to natural human language while drastically reducing the amount of time spent debugging a program by reducing the amount of nuance the program holds in managing memory at any given step in the compilation process. Such issues rarely, if ever, occur with functional/declarative expression.
For one example, recently, I’ve had to get back into using Python for scientific computing for my introductory Physics course at UBC, and in the process of the familiarizing the class to Python, our Physics teaching team held an optional drop-in tutorial on basic algorithmic thinking in Python, with one particular challenge being the second question of ProjectEuler, calculating the sum of all of the even fibonnaci numbers under 4 million in value.
Given such a problem, the standard C solution would be the following:
Now, of course, this could easily be optimized. Assuming we can calculate the number of fibonacci numbers that we will encounter before reaching an even number greater than 4 million (at least, for C, where there are no dynamic arrays), we could generate a lookup table associating the fibonacci number to its index.
Now this method of writing the function doesn’t have much outward time optimization, but certainly makes the program a bit more readable. However, we can do much better than this.
Then, translated to python, the answer would look something like this:
And finally, in Lisp, we see this representation:
If you like my posts, feel free to subscribe to my RSS Feed.