Naive Math Calculator

Sat, Nov 26, 2016

Elixir. It is our responsibility as technologists to feel around and try new things every day. I decided to take foot and invest the $25 dollars on the ‘Programming Elixir’ book by Paul Thomas. I can definitely say that this is money well spent. I have been delighted and blown away by the different ways a programmer can achieve solutions utilizing Elixir’s built in pattern matching, easy tail recursion, built in guard clauses, list comprehensions, shorthands for anonymous function, easy defaults, concurrent processes and so much more!

I decided that I want to talk a little about my naive solution for the last exercise of the strings and binaries section. The problem involves writing a method that can take a List of characters more commonly known as a single quoted string in Elixir and process/parse simple arithmetic in it. Yep like a glorified calculator that would calculate the result of the input '1 + 1'.

The things that I talked about above that I am utilizing include the build guard clauses, easy defaults, and recursion. I began by writing a simple top level method to be called calculate inside of my Math module. This looked like:

defmodule Math do

  def calculate(list) do
  end
end

I knew I would need to parse a list so I set the argument to be called list. The next thing I new I would need to do would be to split up this list. I attempted to do the common [0] access method we all keep near and dear to our hearts in JS or Ruby, but I was not successful. A quick look at the docks led me to the Enum method called split_while. This would fit perfect when iterating recursively through all the characters of '1 + 1'. Now I just had to empower Elixir’s power of recursion to to get me through the list of characters such as:

defmodule Math do

  def calculate(list) do
    _split_up_list(list)
  end

  defp _split_up_list(_, acc \\ [])

  defp _split_up_list([], acc), do: acc

  defp _split_up_list([ head | tail ], acc) when head != ?\s do
  end

  defp _split_up_list([ head | tail ], acc) when head == ?\s do
  end
end

A couple of other things are introduced in the above including the usage of the default for the acc short for accumulator. Honestly, I am not a huge fan of abbreviated variables, but for this book and the code I read I am attempting to follow suit. Another thing is the defp keyword for declaring a private method. Great for organizing a consistent API for the interface of you module. The appearance of the [ head | tail] in the arguments is Elixir’s powerful pattern matching capabilities pulling out the head or the first element of the list and then the tail or a list of the rest of the elements. I am doing this to use the guard clauses for detecting whether or not there is an empty string. The next parts I filled in with the proper methods to access elements in a list (Kernel.elem) and utilized the accumulator to build a list of elements to pass to a function to dynamically evaluate my math statement. After finding the Kernel.apply method I was home free and my naive solution was finally completed:

defmodule Math do

  def calculate(list) do
    split_up_list = _split_up_list(list)
    eval_math(split_up_list)
  end

  defp eval_math(list) do
    operator = Enum.at(list, 1)
    args = [ Enum.at(list, 2), Enum.at(list, 0) ]
    apply(Kernel, operator, args)
  end
  
  defp _split_up_list(_, acc \\ [])

  defp _split_up_list([], acc), do: acc 

  defp _split_up_list([ head | tail ], acc) when head != ?\s do
    list = [ head | tail ]
    split_tuple = Enum.split_while(list, &(&1 != ?\s))
    first_part = elem(split_tuple, 0)
    primitive_first_part = if first_part == '+' || first_part == '-' || first_part == '*' || first_part == '/' do
                             List.to_atom(first_part)
                           else
                             List.to_integer(first_part)
                           end
    second_part = elem(split_tuple, 1)
    acc = [ primitive_first_part | acc ]
    _split_up_list(second_part, acc)
  end

  defp _split_up_list([ head | tail ], acc) when head == ?\s do
    list = [ head | tail ]
    split_tuple = Enum.split_while(list, &(&1 == ?\s))
    # throw away first part which is empty string
    second_part = elem(split_tuple, 1)
    _split_up_list(second_part, acc)
  end
end

Yea! Elixir is way awesome.