Hamming - Exercism Elixir Solution & Tutorial


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated August 2, 2020

This was a relatively quick little problem to calculate the Hamming distance between two DNA strings. The problem boiled down to determining the number of differences between two charlists of the same length. A quick fun one! As a bonus, I show how to do a pull request on the Exercism Elixir repo in the live stream video.

Live solution video

This is my daily live stream video solution to this problem. Sorry about the out of sync audio towards the end of the video, I think my CPU was overloaded by the video encoding and I’ll need to adjust my settings before the stream tomorrow.

Explanation of the solution

Function to calculate the Hamming distance between two charlists

The main part of this challenge was implementing a function that would take in two charlists (list of integer character codes) of equal length and return the number of places in which the lists differ. For example 'GAT' and 'TAG' should have a distance of 2 because 2 characters differ: the first character and the last character.

Basically we need some way to enumerate through the corresponding characters in the list and increment a counter if they are different and leave the counter the same if the characters are the same:

Character 1: {G, T} => chars different => increment the accumulator from 0 to 1
Character 2: {A, A} => chars same => leave accumulator the same
Character 3: {T, G} => chars different => increment the accumulator from 1 to 2

In order for us to easily compare the nth character of both charlists, I made use of Enum.zip/2:

# 71 is the charcode for G, 84 is the charcode for T, etc.
iex> Enum.zip('GAT', 'TAG')
[{71, 84}, {65, 65}, {84, 71}]

After zipping the lists, we can easily compare the nth character of both lists and increment a counter as required by using Enum.reduce/3:

Enum.zip(strand1, strand2)
|> Enum.reduce(0, fn {char1, char2}, acc ->
  if char1 != char2 do
    acc + 1
  else
    acc
  end
end)

Full solution in text form

Here’s the full solution in text form, in case you want to look over the final product:

defmodule Hamming do
  @doc """
  Returns number of differences between two strands of DNA, known as the Hamming Distance.

  ## Examples

  iex> Hamming.hamming_distance('AAGTCATA', 'TAGCGATC')
  {:ok, 4}
  """
  @spec hamming_distance([char], [char]) :: {:ok, non_neg_integer} | {:error, String.t()}
  def hamming_distance(strand1, strand2) when length(strand1) == length(strand2) do
    hamming_distance =
      Enum.zip(strand1, strand2)
      |> Enum.reduce(0, fn {char1, char2}, acc ->
        if char1 != char2 do
          acc + 1
        else
          acc
        end
      end)

    {:ok, hamming_distance}
  end

  def hamming_distance(_strand1, _strand2), do: {:error, "Lists must be the same length"}
end

Comment & Share