Having fun with Lisp(s)

2020-06-07 00:00

A while back, I was commuting across the city to work. It took about an hour and a half to commute one way, so I had a lot of free time on the bus if I wasn’t falling asleep from the same monotonous view every day.

During this time, if I wasn’t sleeping, I was either reading programming books or doing programming puzzles in my notebook. Sometimes the bumps would get the best of me, but what can you do?

There were a few books that I really enjoyed, and I didn’t finish any of them. I kind of just hopped through different pages learning about different things, because these were reference books, not novels, and that’s what do you do with these right? Okay, that was an excuse. Sometimes, my attention span gets the best of me, and I need to move onto something else, so I would always have at least two books with me to jump between.

Before leaving in the morning, I would choose two books, from the four books I was studying, to read on the bus:

Reading SICP

SICP’s language felt like I was reading either a really long computer science riddle, or an ancient wizard’s spell book, so it was hard to focus on that, especially since I don’t have a background in computer science. I think I restarted SICP several times, worrying that I missed something that required me to understand something further in the book haha. I also learned that all of that “useless” math I learned in grade school was suddenly useful to the hobbyist programmer in me as well haha.

Reading The Little Schemer

The Little Schemer is still one of my favourite books. A friend knew I was learning Racket/Scheme, and was kind enough to lend me a hard copy for a few years. I really think this book changed my way of thinking. It teaches you how to solve programming problems using recursion. I had no clue you could solve division problems just with recursion and a (- 1 ...) procedure. That blew my mind!

While I was writing my static website generator, I ended up coming back into The Little Schemer mindset: I wrote a little procedure that recursively checks if all elements in one list exist in another list, and if there are any missing, it returns the missing elements in a new list.

Here’s the code for that procedure below:

;; lst1 is a list of required elements
;; lst2 is a list of elements to check

(define (get-missing lst1 lst2)
  (if (null? lst1)
      null
      (if (member (car lst1) lst2)
          (get-missing (cdr lst1) lst2)
          (cons (car lst1)
                (get-missing (cdr lst1) lst2)))))

I ended up using this procedure to check if all directories or files existed, and if not, my program would offer to “repair” the files or directories for the user.

Reading Living Clojure

Another friend recommended me the Living Clojure book, and had mentioned that, at the time, it was available on HumbleBundle, so I bought it as an ebook. Even though I only got three quarters of the way through it, I feel like it really helped kick-start my Lisp-learning adventure.

Before Clojure, I had started learning Lisp-like languages, such as Hy, but after getting into Clojure, everything felt so great programming-wise. With Hy, it felt like I was trying to force Python habits into Lisp, which didn’t feel right.

Don’t get me wrong, Hy is super cool, and I still want to get back into it eventually. It was just that, at the time, I was really enjoying Clojure (and still do).

One thing I really like about Clojure is its choice on syntax in comparison to other lisps. It’s a lot less verbose.

For example, here’s Clojure’s let statement:

(let [x value] body ...)

in comparison to Racket/Scheme’s let statement:

(let ([x value]) body ...)

and Clojure’s anonymous function:

(fn [x] body ...)

in comparison to Racket/Scheme’s anonymous procedure:

(lambda (x) ...)

It’s just way less typing!

… Okay, the lambda syntax in the last example is only a few letters less, but you get the idea!

Reading How to Design Programs

With this book, I had a similar experience to SICP in that I had kept restarting it, or had just used it as a reference book by jumping between sections. Also, not having a REPL on the bus probably contributed to some of the struggles too, haha.

I had started somewhere in the middle of the book, because some of the beginning problems seemed too easy. I was wrong in doing that, because I missed a lot of the great knowledge HtDP gives you in the beginning of the book, so I’m starting it over by keeping a notebook-like git repository, and working through the exercises properly, just like a school book. It feels like a good compromise between SICP and The Little Schemer, though I still stand in saying that The Little Schemer is still one of my favourite programming books.

After I finish HtDP, if I ever do, it’s a huge book, I plan on tackling SICP, in hopes that I’m better prepared to take on some of its trickier math and mind exercises.

Having fun with recursion

Even after having gone through most of The Little Scheme pretty thoroughly, I still catch myself “doodling” with code once in a while.

One night I was in the mood to fiddle with some recursive programming, but nothing that involved a project or anything serious, just something I could play with and have fun. I decided to make a procedure that “ate” a list of food, and displayed each food item that it ate. After it finished “eating” all the food items in the list, it would “burp”:

##lang racket
(define (eat-everything listof-food)
  (if (null? listof-food)
      (displayln "burp")
      (begin (displayln (string-append "ate " (car listof-food)))
             (eat-everything (cdr listof-food)))))

This is more or less a for loop implemented using recursion, but how it works is it first checks if the list you provide is empty. In racket null or '() both mean the same thing: “empty”. If the list is empty, then print "burp" to the screen; however, if the list is not empty, then the first item of the list is joined with the word "ate " and printed to the user. The procedure then calls the remaining items of the list as its new parameter. The process the repeats, but the first item of the list is now the next item in the list, each time the procedure calls itself.

I still wasn’t done though! I wanted to play more! So I made a picky procedure that eats all items in a list, except for one item of food, and then returns the same list with that item removed:

(define (eat-everything-but food listof-food)
  (if (null? listof-food)
      '()
      (if (equal? (car listof-food) food)
          (eat-everything-but food (cdr listof-food))
          (cons (car listof-food)
                (eat-everything-but food (cdr listof-food))))))

The way this procedure works is similar, but the way it handles the first item in the list it is given is different. In this procedure the first item is checked to see if it equals the food parameter value, which is the item we don’t want our procedure to “eat”, if it equals that item, then the procedure calls itself with the rest of the items in the list as its new parameter.

As an example, the first time around, the parameter could be something like:

'("apple" "orange" "banana")

Then, when the procedure calls itself, it only takes in the rest of the items in the list using the (cdr ..) procedure as a parameter like so:

(cdr '("apple" "orange" "banana"))

which would result in:

'("orange" "banana")

When checking the rest of the items, it, again, checks the first item in the list, which is the next item in the list of the original list we fed the procedure. If the item is not equal to the food parameter value, it adds that item to the list, because it’s an item we want to keep.

The (cons (car listof-food) (eat-everything-but ...) creates a little waiting line of items to be added to a list. When the procedure recursively reaches the end of the list, our procedure returns '(). This is important, because all of the items that were “waiting in line” can now be “consed” onto '() to create a new list like so:

(cons "apples" (cons "bananas" (cons "oranges" '())))

which would return '("apples" "bananas" "oranges").

Wrapping up

Okay, this blog post started to get unstructured really quickly, but I had a lot of fun writing it, and I don’t get to do a lot of unstructured writing as a technical writer! haha.

P.S. Thanks to everyone who has ever helped me understand anything. Programming is such an adventure for me!