Advent of Elixir - Day 05

This challenge was rather boring, I’m not gonna lie. Definitely the least interesting so far, so I decided to spice it up by adding some ExUnit tests to make it a bit more educational. I won’t document those here though. The problem just requires us to parse lines of text with regexps, which means we just need to find the right regexps, which means this will have nothing to do with Elixir.

It is worth mentioning one thing though. I’ve seen a lot of people solving it this way:

  1. define the 3 conditions (3 vowels, duplicate letter, excluded strings);
  2. define a function nice? which checks for the 3 conditions;
  3. apply the function to the input and then count the result;

Well, let me tell you that this is the wrong way to solve the problem. Not because it yields the wrong result, but because it is terribly inefficient. The above solution parses the entire input with 3 expensive operations, when in fact there’s no need to do them in parallel.

The right approach to the problem is to progressively reduce the amount of lines to parse by applying each condition sequentially.

Day 05 Part 01

I have defined the three conditions as follow.

rejects = :binary.compile_pattern(~w(ab cd pq xy))
vowels = ~r/[aeiou].*[aieou].*[aeiou]/
doubles = ~r/(\w)\1/

The rejects variable uses :binary.compile_pattern to make comparison a bit faster. The other two are just plain old regexps that you can write in any language.

Here’s the solution to Part 01 with the sequential filters:

solution to day 05 part 01

File.stream!(“input”)
|> Enum.reject(&String.contains?(&1, rejects))
|> Enum.filter(&Regex.match?(doubles, &1))
|> Enum.filter(&Regex.match?(vowels, &1))
|> Enum.count

We start with Enum.reject because it’s the cheapest operation. After that, doubles and vowels are pretty much interchangeable, I’m not sure which one would be cheaper. I’ve decided to go for doubles first as it seemed more efficient. It would probably be interesting to test the performances of each regexp, but we’d require a larger dataset.

Day 05 Part 02

Even more boring as we only have two regexps to write. Let’s get over with:

solution to day 05 part 01

interleaved = ~r/(\w).\1/
pairs = ~r/(\w\w).*\1/
File.stream!(“input”)
|> Enum.filter(&Regex.match?(interleaved, &1))
|> Enum.filter(&Regex.match?(pairs, &1))
|> Enum.count
Like what you read? Give Marco Ceresa a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.