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. https://github.com/gvaughn/elixir_kata/blob/master/roman/roman_numerals.exs

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.

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

We're in Beta

About us Mission Statement