ryanzidago

ryanzidago

What do you think of my solution for converting integers to roman numerals?

defmodule RomanNumerals do
  import IO, only: [iodata_to_binary: 1]

  @doc """
  Convert the number to a roman number.
  """
  @spec numeral(pos_integer) :: String.t()
  def numeral(0),               do: ""
  def numeral(1),               do: "I"
  def numeral(2),               do: "II"
  def numeral(3),               do: "III"
  def numeral(4),               do: "IV"
  def numeral(5),               do: "V"
  def numeral(6),               do: "VI"
  def numeral(7),               do: "VII"
  def numeral(8),               do: "VIII"
  def numeral(9),               do: "IX"
  def numeral(10),              do: "X"
  def numeral(50),              do: "L"
  def numeral(100),             do: "C"
  def numeral(500),             do: "D"
  def numeral(1000),            do: "M"
  def numeral(n) when n < 40,   do: [numeral(10), numeral(n - 10)]                  |> iodata_to_binary()
  def numeral(n) when n < 50,   do: [numeral(10), numeral(50), numeral(n - 40)]     |> iodata_to_binary()
  def numeral(n) when n < 90,   do: [numeral(50), numeral(n - 50)]                  |> iodata_to_binary()
  def numeral(n) when n < 100,  do: [numeral(10), numeral(100), numeral(n - 90)]    |> iodata_to_binary()
  def numeral(n) when n < 400,  do: [numeral(100), numeral(n - 100)]                |> iodata_to_binary()
  def numeral(n) when n < 500,  do: [numeral(100), numeral(500), numeral(n - 400)]  |> iodata_to_binary()
  def numeral(n) when n < 900,  do: [numeral(500), numeral(n - 500)]                |> iodata_to_binary()
  def numeral(n) when n < 1000, do: [numeral(100), numeral(1000), numeral(n - 900)] |> iodata_to_binary()
  def numeral(n) when n < 5000, do: [numeral(1000), numeral(n - 1000)]              |> iodata_to_binary()
end

Showing Posts 1 to 10

collegeimprovements

collegeimprovements

Elegant

kokolegorille

kokolegorille

Nice, but your spec is wrong. It should be non_neg_integer() as You do include 0 :slight_smile:

ryanzidago

ryanzidago OP

Nice catch!

Thanks @kokolegorille ! This is for such comments that I like to post on this forum.

NobbZ

NobbZ

Also, the spec does make the function appear unbound towards positive infinity.

In reality the spec should be numeral(0..4999) :: String.t().

In general, I do consider this appraoch as quite noisy, and also since iodata_to_binary/1 is used on each recursion step, nothing is gained by it. It might even perform worse than just using <>/2.

Last but not least, I’m not a friend of not using mix format.

ryanzidago

ryanzidago OP

Thanks @NobbZ, I’ll definitely pay more attention to the spec!

Then, I’ll refactor using <>/2 instead of iodata_to_binary/1.

I also generally use mix format; but wanted to make an exception. I find it more readable this way.

ryanzidago

ryanzidago OP

Also with the way you defined the spec, could it be confused with a range?

How would you write the spec for a function that takes a range of integer from 0 to 4999 then?

LostKobrakai

LostKobrakai

This already is how you‘d do that in a typespec: Typespecs reference — Elixir v1.20.2

NobbZ

NobbZ

There is Range.t/2 for that.

lud

lud

You could rename your function def numeral to defp do_numeral, and then define a function def numeral(n), do: iodata_to_binary(do_numeral(n)) so you have a single call to iodata_to_binary.

sfusato

sfusato

You could write a macro :innocent: that by using your function will generate 3999 functions for each number in the range 1-3999 (took the 3999 figure from Wikipedia).

The largest number that can be represented in this notation is 3,999 (3,000 + 900 + 90 + 9 = MMM + CM + XC + IX = MMMCMXCIX )

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews