tio407

tio407

Converting to Roman Numerals exercise; need help understanding code

I have a hard time following the code at defp numeral function. Is this well written Elixir code or is it better to use pipes?

defmodule Roman do
  @numerals [M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1]

  @doc """
  Convert the number to a roman number.
  """
  @spec numerals(pos_integer) :: String.t()
  def numerals(number) do
    numeral([], number, @numerals)
  end

  defp numeral(curr_nums, number, [{letter, digit}|_t] = num_list) when number >= digit do
    numeral([letter | curr_nums], number - digit, num_list)
  end

  defp numeral(curr_nums, number, [_h|t]) do
    numeral(curr_nums, number, t)
  end

  defp numeral(numerals, _n, []) do
    numerals
      |> Enum.reverse
      |> Enum.map_join("", &to_string/1)
  end
end

What are some suggestions for re-writing this in a better way? It’s my old code but I no longer understand what is happening.

Marked As Solved

kip

kip

ex_cldr Core Team

I think its fine except the variable naming makes it a bit hard to see what’s happening. And the odd comment helps. Personally I think a recursive function like this is a good fit for the problem. Perhaps like this:

defmodule Roman do
  @roman_to_decimal_mappings [M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1]

  @doc """
  Convert the number to a roman number.
  """
  @spec numerals(pos_integer) :: String.t()
  def numerals(number) do
    numeral([], number, @roman_to_decimal_mappings)
  end

  # While the number is >= the decimal value at the head of the mappings,
  # accumulate the Roman numeral and decrement the number
  defp numeral(roman_acc, number, [{roman_numeral, decimal_value}| _t] = roman_to_decimal_mappings) 
      when number >= decimal_value do
    numeral([roman_numeral | roman_acc], number - decimal_value, roman_to_decimal_mappings)
  end

  # When the number is less than the decimal value at the head of the
  # mapping list, move to the next mapping pair
  defp numeral(roman_acc, number, [_h | remaining_mappings]) do
    numeral(roman_acc, number, remaining_mappings)
  end

  # And when the mapping list is exhausted, reverse the accumulated
  # Roman numerals and join then
  defp numeral(roman_acc, _n, [] = _remaining_mappings) do
    roman_acc
      |> Enum.reverse
      |> Enum.map_join("", &to_string/1)
  end
end

Also Liked

gregvaughn

gregvaughn

I’m not certain how readable this is for others, but it’s how I worked through the problem early in my Elixir adventures, and I can still explain the approach 6 years later. elixir_kata/roman/roman_numerals.exs at master · gvaughn/elixir_kata · GitHub

I iterate over the mappings and use Enum.reduce with a 2-tuple to keep track of the remainder of the number (after dividing out each Roman value) plus the accumulated Roman numeral string.

Last Post!

collegeimprovements

collegeimprovements

defmodule RomanNumerals do
  @spec to_roman(integer) :: String.t()
  def to_roman(number) when is_integer(number) and number > 0 do
    to_roman_recursive(number, "")
  end
  def to_roman(_), do: ""

  defp to_roman_recursive(0, acc), do: acc
  defp to_roman_recursive(number, acc) when number >= 1000, do: to_roman_recursive(number - 1000, acc <> "M")
  defp to_roman_recursive(number, acc) when number >= 900, do: to_roman_recursive(number - 900, acc <> "CM")
  defp to_roman_recursive(number, acc) when number >= 500, do: to_roman_recursive(number - 500, acc <> "D")
  defp to_roman_recursive(number, acc) when number >= 400, do: to_roman_recursive(number - 400, acc <> "CD")
  defp to_roman_recursive(number, acc) when number >= 100, do: to_roman_recursive(number - 100, acc <> "C")
  defp to_roman_recursive(number, acc) when number >= 90, do: to_roman_recursive(number - 90, acc <> "XC")
  defp to_roman_recursive(number, acc) when number >= 50, do: to_roman_recursive(number - 50, acc <> "L")
  defp to_roman_recursive(number, acc) when number >= 40, do: to_roman_recursive(number - 40, acc <> "XL")
  defp to_roman_recursive(number, acc) when number >= 10, do: to_roman_recursive(number - 10, acc <> "X")
  defp to_roman_recursive(number, acc) when number >= 9, do: to_roman_recursive(number - 9, acc <> "IX")
  defp to_roman_recursive(number, acc) when number >= 5, do: to_roman_recursive(number - 5, acc <> "V")
  defp to_roman_recursive(number, acc) when number >= 4, do: to_roman_recursive(number - 4, acc <> "IV")
  defp to_roman_recursive(number, acc) when number >= 1, do: to_roman_recursive(number - 1, acc <> "I")
end

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42716 114
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

We're in Beta

About us Mission Statement