One of the Exercism exercises on the Elixir track is to take an integer (0 to ~3000) and convert it to a Roman numeral. My solution was a little “pipe-happy,” and I have subsequently discovered better approaches. In the course of doing all that piping, I used Enum.reverse()
twice. I’ve read that sometimes list reversal is O(n) and sometimes O(1) with a pointer swap, but I don’t know which applies in Elixir. Should I even care? Or perhaps a better question, at what length of list should I start caring?
For reference, my solution:
defmodule RomanNumerals do
@doc """
Convert the number to a roman number.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) when is_integer(number) do
number
|> Integer.digits()
|> Enum.reverse()
|> Enum.with_index()
|> Enum.map(&get_roman/1)
|> Enum.reverse()
|> List.to_string()
end
def get_roman({_number, _index} = pair) do
case pair do
{0, _} -> ""
{1, 0} -> "I"
{2, 0} -> "II"
{3, 0} -> "III"
{4, 0} -> "IV"
{5, 0} -> "V"
{6, 0} -> "VI"
{7, 0} -> "VII"
{8, 0} -> "VIII"
{9, 0} -> "IX"
{1, 1} -> "X"
{2, 1} -> "XX"
{3, 1} -> "XXX"
{4, 1} -> "XL"
{5, 1} -> "L"
{6, 1} -> "LX"
{7, 1} -> "LXX"
{8, 1} -> "LXXX"
{9, 1} -> "XC"
{1, 2} -> "C"
{2, 2} -> "CC"
{3, 2} -> "CCC"
{4, 2} -> "CD"
{5, 2} -> "D"
{6, 2} -> "DC"
{7, 2} -> "DCC"
{8, 2} -> "DCCC"
{9, 2} -> "CM"
{1, 3} -> "M"
{2, 3} -> "MM"
{3, 3} -> "MMM"
_ -> "Too High!"
end
end
end
And a more elegant solution by one of the other students:
defmodule RomanNumerals do
@doc """
Convert the number to a roman number.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(n) do
cond do
n >= 1000 -> "M" <> numeral(n - 1000)
n >= 900 -> "CM" <> numeral(n - 900)
n >= 500 -> "D" <> numeral(n - 500)
n >= 400 -> "CD" <> numeral(n - 400)
n >= 100 -> "C" <> numeral(n - 100)
n >= 90 -> "XC" <> numeral(n - 90)
n >= 50 -> "L" <> numeral(n - 50)
n >= 40 -> "XL" <> numeral(n - 40)
n >= 10 -> "X" <> numeral(n - 10)
n >= 9 -> "IX" <> numeral(n - 9)
n >= 5 -> "V" <> numeral(n - 5)
n >= 4 -> "IV" <> numeral(n - 4)
n >= 1 -> "I" <> numeral(n - 1)
true -> ""
end
end
end