호두나무 공방/Exercism in Elixir

Roman Numerals - Exercism in Elixir

2022. 6. 7. 22:11

문제 보기

10진수 숫자를 로마자로 변환하는 문제였다. 구현에 고민을 제법 한 문제였다. 재귀를 쓸 수도 있고 그 외에도 여러 가지 방법을 쓸 수 있었을 것 같다(다 풀고 발견한 것이었지만 이 문제는 재귀를 연습하기 위해 만들어진 듯하다).

이번에 Exercism을 풀면서는 전체적으로 매핑의 가짓수가 늘어나더라도(예를 들어 지금은 천 단위 숫자까지만 변환하면 됐지만 이후 대응해야 하는 수의 크기가 커지는 경우를 생각) 코드 수정 없이 데이터만 수정하면 되도록 해보고 싶어서 데이터 부분을 별도 맵으로 분리해서 코드를 짜 보았다.

defmodule RomanNumerals do
  @exp_to_roman_mapping %{
    0 => %{1 => "I", 4 => "IV", 5 => "V", 9 => "IX"},
    1 => %{1 => "X", 4 => "XL", 5 => "L", 9 => "XC"},
    2 => %{1 => "C", 4 => "CD", 5 => "D", 9 => "CM"},
    3 => %{1 => "M"},
  }
  @doc """
  Convert the number to a roman number.
  """
  @spec numeral(pos_integer) :: String.t()
  def numeral(number) do
    Integer.digits(number)
    |> Enum.reverse()
    |> Enum.with_index()
    |> Enum.map(&to_roman/1)
    |> Enum.reverse()
    |> Enum.join("")
  end

  defp to_roman({digit, exp}) do
    case digit do
      d when d in [4, 5, 9] -> @exp_to_roman_mapping[exp][d]
      d when d > 5 -> @exp_to_roman_mapping[exp][5] <> String.duplicate(@exp_to_roman_mapping[exp][1], d - 5)
      d -> String.duplicate(@exp_to_roman_mapping[exp][1], d)
    end
  end
end