I made two attempts at this problem before I was able to get it right.
The first was a really ugly one with lots of mutation. I did this out of pure spite. It gave me the correct Part 1 solution but was too slow to complete Part 2 (I even left it running overnight!).
Then I read in the Racket docs that using set!
to mutate a variable can lead to bad performance. So I wrote a nice immutable FP solution. This also gave me the correct Part 1 answer and was also too slow to complete Part 2 (tried the overnight thing again!).
At this point, I was a little frustrated. I was always more interested in keeping up with each day’s puzzles than in getting super-optimized solutions, but now I’m falling behind and am once again faced with a situation where I have to optimize or I can’t proceed.
On the subreddit it seems generally people are using linked lists to solve the problem. Which makes sense because the problem is all about navigating and modifying an ever-growing circular list. But Racket doesn’t have pointers and it heavily discourages mutation, so that didn’t seem to be an option.
I eventually stumbled upon the concept of the zipper as a functional alternative to the linked list, providing similar speed improvements. Racket has a zippers
library but it seems calibrated to accomodate the most complicated use-cases (binary trees and arbitrary structs etc), and still being unfamiliar with how exactly this zipper concept is supposed to work I could not determine from its docs how to actually use it. (This is a common failing of Racket documentation: every function is thoroughly explained in technical detail but there are no code samples demonstrating how to plug it all together.)
I brought my problem to the subreddit; I’d gotten as far as to identify an approach that seemed promising, I just needed help understanding how to apply it. The tipping point was getting this response at about the same time as I found this old mailing list post.
In the end, I got the circular zipper implemented and it did end up being far faster: day09.rkt
This was the hardest day so far in that I ran up against a performance problem that I initially felt I had no tools for solving. In the end it forced me to learn a new concept that has made me a better programmer. I would still like to understand more clearly why the zipper approach was so much faster than my other immutable list-based approach. Maybe I will pose that question to the Racket users group.