2020-12-18 08:27:16 +00:00
|
|
|
Day 18 Notes
|
|
|
|
|
|
|
|
+--------+
|
|
|
|
| Part 1 |
|
|
|
|
+--------+
|
|
|
|
|
|
|
|
$ elixir day18part1.exs
|
2020-12-20 06:31:28 +00:00
|
|
|
21347713555555
|
2020-12-18 08:27:16 +00:00
|
|
|
|
|
|
|
Thoughts:
|
|
|
|
Since the order of precedence is the same, we can walk the token list, building up a left operand
|
|
|
|
and operator as we go, then applying the operator as soon as we get the right operand. For groups,
|
|
|
|
use recursion to resolve the group, and push the result back onto the beginning of the token list.
|
|
|
|
|
|
|
|
|
|
|
|
+--------+
|
|
|
|
| Part 2 |
|
|
|
|
+--------+
|
|
|
|
|
|
|
|
$ elixir day18part2.exs
|
2020-12-20 06:31:28 +00:00
|
|
|
275011754427339
|
2020-12-18 08:27:16 +00:00
|
|
|
|
|
|
|
Thoughts:
|
|
|
|
|
|
|
|
Now the order of precedence is relevant, so can no longer just walk the token list, need to know the
|
|
|
|
full context.
|
|
|
|
Addition basically becomes implicit brackets.
|
2020-12-20 06:31:28 +00:00
|
|
|
|
|
|
|
If we have a * operand, for example 2 in 1 * 2 + 3, we can't apply * without first looking ahead
|
|
|
|
to see if there is a higher precedence +.
|
|
|
|
|
|
|
|
I decided to build an Abstract Syntax Tree, and then evaluate it. To build the AST, I used the
|
|
|
|
following strategy:
|
|
|
|
|
|
|
|
1. First pass - resolve + operations.
|
|
|
|
For numbers and * operators, store them in a _later_ buffer until we know what to do with them.
|
|
|
|
For + operators, we can safely add the previous operand in the later buffer with the next
|
|
|
|
operand.
|
|
|
|
Recurse into brackets.
|
|
|
|
2. Second pass - resolve * operations.
|
|
|
|
The second pass runs at the end of any bracket group, or the end of string.
|
|
|
|
All the additions have been handled, so can safely just multiply these.
|
|
|
|
3. Finally, evaluate the AST recursively to get the answer.
|
|
|
|
|
|
|
|
I spent far too long on this and it was very difficult to debug. The examples provided were very
|
|
|
|
useful and hit a lot of edge cases. Again, I really struggle to reason about any form of recursion
|
|
|
|
that is more complex than a simple loop. There were lots of IO.inspects going on in the development
|
|
|
|
of this. The answer can probably be simplified further, but I'm pretty happy that I managed to
|
|
|
|
implement it using an AST with multiple function clauses as a proof of concept.
|
|
|
|
|
|
|
|
It's pretty hard to tell what all the clauses do at first glance, but then that would be true for a
|
|
|
|
a huge function with lots of embedded conditionals too. I will definitely be seeing how other people
|
|
|
|
implemented this one.
|
2020-12-18 08:27:16 +00:00
|
|
|
|
|
|
|
+------------------+
|
|
|
|
| Overall Thoughts |
|
|
|
|
+------------------+
|
|
|
|
|
2020-12-20 06:31:28 +00:00
|
|
|
Part 1 was very simple due to it not having precedence, and I think it can be implemented nicely in
|
|
|
|
Elixir. Part 2's deceptively simple requirements were surprisingly difficult - probably because of
|
|
|
|
the approach I took with building an AST.
|
2020-12-18 08:27:16 +00:00
|
|
|
|
2020-12-20 06:31:28 +00:00
|
|
|
Considering I have less time to spend on the last week of questions, I decided to chip away at this
|
|
|
|
one over a few days. Now I'm spoilt for choice for which one to do next! 17, 19, or 20...
|
2020-12-21 03:22:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
Update: this solution is great. Wish I'd thought of it. What I was trying to do here, but simple and
|
|
|
|
elegant: https://elixirforum.com/t/advent-of-code-2020-day-18/36300/6
|