Unnecessary

Projects

About

Advent of Code 2024 - Day1

Dec 1, 2024

It’s time once again for Advent of Code. This time I am trying to use it as an opportunity to get better writing Gleam.

Today’s problem involved taking two lists of integers, presented in an input file with one entry from each list separated by three spaces on each line of input, and manipulating these lists to answer a few questions. The first question required we sort the lists and then find the absolute value of the difference between the elements of each list in sorted order. The second part require that we count up the number of occurances of each element from the first list in the second list and sum up the multiple of the value from the first list with the number of occurances in the second list.

My initial solution is shown here. I separated it into three parts. First, the parse_line function will transform a line of text from the input file into a list of integers, splitting on one or more spaces. The second part is to use this function in the parse_lines function to create a list of lists of integers, one per input text file line and then pull out the first element from each sublist to create the first list, and the second element from each sublist to create the second list. You can see my first attempt at this function was a little clunky. I used a separate let statement for each list and went through the combined list of lists twice, once for each list. In the end I return a tuple containing the two separate lists.

Finally, in the solve_p1 and solve_p2 functions you can see how I used the resulting lists to either find the different in the sorted pairs, or to count the occurances from one list in the next. I went to bed and left refactoring to the morning.

The changes I made during refactoring can be seen in this code comparison. I made a new function called solve_p2b which contains my refactored solution for counting occurances in the second list. I used a dictionary to count up all the elements in the second list first, and then iterated through the first list looking up counts from the dictionary. This is much more efficient. I also first found it a little difficult to deal with the Result from using dict.get to see if there was an existing entry in the dictionary or if I should just assume there were 0 entries. One thing I missed from Go is the idea that maps will always return a default 0 value, however I quickly found that I could get similar results in Gleam by using the result.unwrap function to replace an Error type with a literal 0. Effectively this means that if you care if an entry is present in a map or dictionary in Go you would write

if i, ok := myMap[myKey]; ok {
  // The value was found in the map
  // ...
} else {
  // The value was not found in the map
  // ...
}

and in Gleam that would be

case dict.get(my_dict, my_key) {
  Ok(i) -> // The value was found and is i ...
  Error(Nil) -> // The value was not found ...
}

However, if you just want to assume any value not found is 0, then in Go it is

i := myMap[myKey]

and in Gleam is it as simple as

let i = dict.get(my_dict, my_key) |> result.unwrap(0)

In both cases the language lets you set a default value very concisely, but in the Gleam language it is much more explicitly set.

The other thing I didn’t like was the way I parsed the lines from the input. I ended up refactoring this quite a bit, which can be seen in the comparison of lines 57-79 of the initial code to lines 77-86 of the final code. I had used list.fold to build the dictionary in the prior refactoring, and I realized that I could similarly build up the tuple of separate lists from the combined list using fold as well. In order to do some error checking, namely that each line had exactly two integers, I used list.try_fold which would return an error if it encountered any and I used a case statement to match on lines with two integer entries. The resulting parse_lines function is much more concise and I believe more readable to someone who is used to functional programming.

Overall I find I tend to still think very procedurally, which at times leads me to more complex solutions. I hope that as I work through the rest of Advent of Code I can get better at recognizing the best way to perform the data manipulation in a more functional way.

Tags: #gleam #programming