Advent of Code - 2021
It’s time, once again, for the third annual tatekennington.com write up of last years Advent of Code competition. Now officially a website tradition, based on the assumption that something is legally a tradition after it has happened at least three times. This year I found myself much busier over the holiday season and mostly forgot about Christmas. So now, well into January, I’m recapturing that festive spirit by leisurely retreading the Advent of Code post-mortem.
Once again my weapon of choice is Rust. Although I ostensibly wanted to carry on the not-tradition of using a different language every year, no newcomers were able to catch my eye. Last year I was still very new to Rust and remarked that I didn’t think it was the most suitable language for competition programming. To some extent this is still true, having to unwrap so often isn’t ideal and some of the string processing could be nicer. On the other hand, once you become more familiar with Rust’s idioms the language becomes far more expressive than I initially gave it credit for. Sometimes when solving problems it felt like the implementation flowed in a way I rarely experience in other languages, and if iterators were my highlight of last year, then this year it was pattern matching that was the standout.
Day 1
For the first day, input is given as a list of numbers representing depth measurements. For the first part, you have to count how many times the depth has increased from the previous measurement, and for part two you have to do the same but instead of individual depth measurements, you use the sum of the next three depth measurements. Thinking back to last year this is a problem that can be solved easily with the window
iterator. For part one you can use a window
with size two, then filter
the results on the second element in the window being greater than the first, and return the count
. For part two you can use a window
with size three, then map
to the sum of all three elements in the window, and finally calculate the number of increases using the same strategy as part one.
If you’re not very familiar with iterators you might be confused why you would solve the problem this way, instead of having something like a for loop which computes all this logic in one place. One important thing to understand about iterators, in Rust at least, is that they’re lazy. This means that they’re not actually executed until the resulting value is needed, in this case when we’re counting how many items are in the final list. As such, there’s no overhead to stacking multiple iterators on top of each other, like I do in this solution, it should run as fast as a loop over that list (function call overhead aside). The advantage of iterators then is that they can express this kind of complex and layered logic in a much simpler way than the equivalent “bare” loop. This is part of the expressiveness I mentioned earlier, there’s an almost conversational style to writing code like this; it’s like you’re writing the algorithm instead of the implementation.