sasajuric

sasajuric

Author of Elixir In Action

Advent of Code - Day 5

Note by the Moderators: This topic is to talk about Day 5 of the Advent of Code.

For general discussion about the Advent of Code 2018 and links to topics of the other days, see this topic.


This is my Day 5. My other days are a bit of a mess (constantly refactoring to try out different ways to make it look better). With day 5 though, I saw a lot of places for a bit of optimization like storing the length in the fully_react reduction.

Most Liked

Gazler

Gazler

Phoenix Core Team

Day 5 fits really nicely with binary pattern matching:

chrismcg

chrismcg

My original solution was a pretty hacky regexy thing in rust. After I’d finished and looked at the solutions mega thread the Haskell foldr five liner at the top was a bit of a blow :smile:. I made an elixir version which uses lists rather than binaries which I think is nice too:

(EDIT: very similar to Ryan’s solution above too)

defmodule Day5 do
  @moduledoc """
  Day 5 Elixir solutions for Advent of Code 2018
  """

  @doc """
  Removes all reacting units from the input and returns the final length

  Algorithm from a Haskell implementation in reddit:
  https://www.reddit.com/r/adventofcode/comments/a3912m/2018_day_5_solutions/eb4dchg/

  ## Examples

      iex> Day5.react("dabAcCaCBAcCcaDA")
      10

  """
  def react(input) do
    input
    |> String.to_charlist()
    |> List.foldr([], &react/2)
    |> Enum.count()
  end

  # two units react when the difference in their ASCII code is 32
  defguard reacts(unit, prev_unit) when abs(unit - prev_unit) == 32

  # handle the start case
  defp react(unit, []), do: [unit]
  # handle when the last two elements in the list react
  defp react(unit, [prev_unit]) when reacts(unit, prev_unit), do: []
  # if there's a reaction just return the tail to remove reacting elements
  defp react(unit, [prev_unit | tail]) when reacts(unit, prev_unit), do: tail
  # all good, just add the unit to the list
  defp react(unit, tail), do: [unit | tail]
end

simon

simon

I’ve been astonished at the speed of some of José’s solutions. I don’t think any of mine are ridiculously slow but comparatively they are.

Solving the problems is one element of the challenge. Making the code faster is another. Making it faster and still understandable is where I struggle the most as an Elixir newbie.

My plan is to re-do the challenges in January to see how my code has evolved over the challenge which is not a long period of time but I’m writing a lot of code and learning new things every day.

simon

simon

My take-away from day 5’s code is to keep things simple. And read all of the documentation because I’ve been creating functions when I don’t need to.

My first attempt at the challenge was to do a single pass through the characters in pairs, remove them if the fitted the matching criteria which created a new string to parse and the changing the current ‘cursor’ position to handle the string length change so that I didn’t skip letters. It worked but it was as messy as it sounds (possibly more-so!) and it was really slow.

Solving part 2 whilst part one is slow is possible but I’d have probably had time for a nap whilst it ran so I decided to try a simpler approach. This time I converted the polymers into a character list (I originally used graphemes but saw one of a few great tricks in @sionide21’s solution… see below) so that I could use recursive pattern matched functions to process the polymers. If at the end of a run through the characters the resulting string was shorter than the original I’d re-process the characters and carry on until changes had been exhausted. This was much, much faster.

Part 2 was where I encountered some new standard library functions and in particular Enum.uniq/1 and Enum.reject/2, both of which allowed me to remove Enum.reduce/3 calls. The combination of Map.values/1 and Enum.min/1 were also really helpful at the end of the pipeline and saved me from writing yet another Enum.reduce/3.

After writing my code I looked at @sionide21’s solution which helped me tidy a few things up. He’d used recursive pattern matching functions too but passed a boolean flag between them to indicate whether the polymers had been edited. This is cleaner. He also took advantage of character codes to handle upper and lower case checks: abs(a - b) == 32. I’d used an if statement inside a single function but I thought of @pragdave and changed it and could then use two functions. If I’d retro-fitted the change flag in place of my string length parameter I could have done the same for the last function.

Overall I was really pleased with how today’s solution turned out and it was gratifying to see that I was heading in the same direction as more experienced Elixir developers.

sasajuric

sasajuric

Author of Elixir In Action

Here’s my day5.

Where Next?

Popular in Challenges Top

sasajuric
Note: This topic is to talk about Day 12 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New
QuinnWilton
Note: This topic is to talk about Day 7 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can joi...
New
New
bjorng
Note: This topic is to talk about Day 25 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
bjorng
Note: This topic is to talk about Day 1 of the Advent of Code 2019.
New
bjorng
Note: This topic is to talk about Day 13 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
christhekeele
Setting this down for the night, as after a quick naive solve for quick part 1 I realize that part 2 is by design computationally expensi...
New
lud
Gosh this one took me sooo much time. At first I was trying to iterate each digit independently on the input A number to make digits cha...
New
New
igorb
Today is a brute-force day: advent-of-code-2024/lib/advent_of_code2024/day6.ex at main · ibarakaiev/advent-of-code-2024 · GitHub Takes a...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement