Last active
April 11, 2022 12:00
-
-
Save bapti/f5eb055bad5b09e601f57dcced24e1b5 to your computer and use it in GitHub Desktop.
Arabic to Roman Numeral converter in elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RomanNumeralFirstPass do | |
def convert(value) do | |
sum = | |
String.graphemes(value) | |
|> convert_numerals() | |
|> Enum.sum() | |
{:ok, sum} | |
end | |
defp convert_numerals(list_of_numerals, numerical_values \\ []) | |
defp convert_numerals([first | []], numerical_values) do | |
converted_numeral = convert_numeral(first) | |
convert_numerals([], [converted_numeral | numerical_values]) | |
end | |
defp convert_numerals([first, second | tail], numerical_values) do | |
converted_first = convert_numeral(first) | |
converted_second = convert_numeral(second) | |
if converted_first < converted_second do | |
convert_numerals(tail, [converted_second - converted_first | numerical_values]) | |
else | |
convert_numerals([second | tail], [converted_first | numerical_values]) | |
end | |
end | |
defp convert_numerals([], numerical_values), do: numerical_values | |
defp convert_numeral(character) do | |
case character do | |
"i" -> 1 | |
"v" -> 5 | |
"x" -> 10 | |
"l" -> 50 | |
"c" -> 100 | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RomanNumeral do | |
def convert(roman_numeral) when is_binary(roman_numeral) do | |
roman_numeral = String.downcase(roman_numeral) | |
if roman_numeral =~ ~r/[^ivxlc]/i do | |
raise "Invalid roman numeral #{roman_numeral}" | |
end | |
arabic_value = roman_numeral |> String.graphemes() |> sum(0) | |
{:ok, arabic_value} | |
end | |
defp sum([], acc), do: acc | |
defp sum([x], acc), do: sum([], to_arabic(x) + acc) | |
defp sum([x, y | tail], acc) do | |
case {to_arabic(x), to_arabic(y)} do | |
{a, b} when a < b -> sum(tail, acc + b - a) | |
{a, _b} -> sum([y | tail], acc + a) | |
end | |
end | |
defp to_arabic("i"), do: 1 | |
defp to_arabic("v"), do: 5 | |
defp to_arabic("x"), do: 10 | |
defp to_arabic("l"), do: 50 | |
defp to_arabic("c"), do: 100 | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RomanNumeralRefactor do | |
def convert(value) do | |
sum = | |
String.graphemes(value) | |
|> sum_numerals(0) | |
{:ok, sum} | |
end | |
defp sum_numerals(["i", "v" | tail], sum), do: sum_numerals(tail, sum + 4) | |
defp sum_numerals(["i", "x" | tail], sum), do: sum_numerals(tail, sum + 9) | |
defp sum_numerals(["x", "l" | tail], sum), do: sum_numerals(tail, sum + 40) | |
defp sum_numerals(["x", "c" | tail], sum), do: sum_numerals(tail, sum + 90) | |
defp sum_numerals(["i" | tail], sum), do: sum_numerals(tail, sum + 1) | |
defp sum_numerals(["v" | tail], sum), do: sum_numerals(tail, sum + 5) | |
defp sum_numerals(["x" | tail], sum), do: sum_numerals(tail, sum + 10) | |
defp sum_numerals(["l" | tail], sum), do: sum_numerals(tail, sum + 50) | |
defp sum_numerals(["c" | tail], sum), do: sum_numerals(tail, sum + 100) | |
defp sum_numerals([], sum), do: sum | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RomanNumeral do | |
def convert(roman_numeral) when is_binary(roman_numeral) do | |
roman_numeral = roman_numeral |> String.downcase() | |
if roman_numeral =~ ~r/[^ivxlc]/i do | |
raise "Invalid roman numeral #{roman_numeral}" | |
end | |
arabic_value = roman_numeral |> sum(0) | |
{:ok, arabic_value} | |
end | |
defp sum("iv" <> rest, acc), do: sum(rest, acc + 4) | |
defp sum("ix" <> rest, acc), do: sum(rest, acc + 9) | |
defp sum("xl" <> rest, acc), do: sum(rest, acc + 40) | |
defp sum("xc" <> rest, acc), do: sum(rest, acc + 90) | |
defp sum("i" <> rest, acc), do: sum(rest, acc + 1) | |
defp sum("v" <> rest, acc), do: sum(rest, acc + 5) | |
defp sum("x" <> rest, acc), do: sum(rest, acc + 10) | |
defp sum("l" <> rest, acc), do: sum(rest, acc + 50) | |
defp sum("c" <> rest, acc), do: sum(rest, acc + 100) | |
defp sum("", acc), do: acc | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was a code test for a contract position I was applying for in 2022.
My first solution took a little while to get there, I started off TDD-ing and doing a very simplistic approach to the problem. I also hadn't seen the patterns of a roman numeral that if there's a pair of numbers together, like
ix
oriv
the case is that the left is lower than the right. I'd have rather gone the route of special cases like I did with solution 2 and 3 but was directed to write the logic.I added solution 1a as a refactored version of 1 that maintains the logic of the numerals, but makes better use of pattern matching, I still think version 3 is the neatest solution, I think the 4 special cases (would be 6 if we included M and D) is the easiest to follow in terms of decoding a roman numeral.
Notes to future me