Advent of Code 2020 (Days 1-9)
Advent of Code 2020 is here! This year I’m revisiting Julia, a cross between Python, Fortran, with a hint of Lisp. My solutions are on SourceHut, and in this and follow-up posts I’ll keep a running log of my approach and thoughts (edit: I go into much more detail for later, harder problems).
My immediate idea was to take advantage of Julia’s first-class matrix/vector operations. This made quick work of both parts, although as a colleague pointed out to me it could have yielded false positives, since I included each number plus itself (i.e. I really should have filtered out the diagonals).
- Racket: Having a function to produce
combinationsin the standard library made it pretty easy.
- Common Lisp: Writing a function to enumerate two-item combinations for part 1 was easy; three-item combinations for part 2 was trickier, and I ended up just doing all three-element permutations instead.
Julia has some nice features I was able to use here, including pipelines (
which can also be done per-element (
.|>, what Julia calls “broadcasting”). For
example, the following nested function calls:
can be rewritten as:
eachline("input/day02.txt") .|> parseline .|> fn |> sum |> println
In addition to being easier to read, it helps for rapid interactive development and debugging.
Julia’s strong array support came in helpful here.
- Common Lisp:
loopreally shined here, especially the way parallel counters work. Looping over the tree map was very easy and clear:
(defun trees (m xs ys) (loop for j below (array-dimension m 0) by ys for i by xs count (aref m j (mod i (array-dimension m 1)))))
Notice how no upper limit needs to be set for the column counter
the row counter
j solely terminate the loop.
Julia has a solid regex library. Also notable, it allows “chained” comparison operators, e.g.
valid_byr(p) = 1920 <= parse(Int,p["byr"]) <= 2002
- Common Lisp: I experimented with
struct, it went ok. Parsing fields seemed a bit awkward, probably room for improvement there.
As they say, there are 10 types of people in the world: those who speak binary, and those who don’t.
Julia fully embraces unicode. Sometimes it’s clearer using mathematical symbols,
but in general probably not. I like using them anyways, (like how I use λ
lambda in racket). It’s nice that the REPL and emacs mode support
LaTϵχ commands followed by
<tab> to quickly insert them (e.g.
\cup -> ∪ for
\cap -> ∩ for intersection).
A naive parser and some regex solved this. Bonus: I had my program output a
dot file for graphing in Graphviz, but the output
dot was gigantic and unreadable. I thought maybe
circo would make a
nice graph, but I gave up on it after about a half hour.
Is this the next intcode‽ Maybe not… I invested a
bit into learning Julia’s
struct and multi-dispath methods just in case.
Ugh, I hate when you make an assumption during part one that makes you rewrite a
bunch of code for part two. In this case I thought I was clever (“oh, we only
need the last n entries? I’ll write a ring buffer!"), but then of course we
needed all the entries in part two. Admittedly the code wasn’t really that more
complicated just indexing into a
Vector, and I got rid of all the
Ring code I’d written. Learned a bit more about methods and
multi-dispatch. Did I mention how much I love the
[email protected] and
macros? Well they’re nice.
My Day 10 writeup got a bit long, so I broke it into its own post. I’m going to try to do more detailed write-ups like that going forward.