exercism/elixir/say/lib/say.ex

75 lines
2.1 KiB
Elixir

defmodule Say do
@in_english_19 %{
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
6 => "six",
7 => "seven",
8 => "eight",
9 => "nine",
10 => "ten",
11 => "eleven",
12 => "twelve",
13 => "thirteen",
14 => "fourteen",
15 => "fifteen",
16 => "sixteen",
17 => "seventeen",
18 => "eighteen",
19 => "nineteen"
}
@in_english_100 %{
2 => "twenty",
3 => "thirty",
4 => "forty",
5 => "fifty",
6 => "sixty",
7 => "seventy",
8 => "eighty",
9 => "ninety"
}
@doc """
Translate a positive integer into English.
"""
@spec in_english(integer) :: {atom, String.t()}
def in_english(number) when number < 0 or number > 999_999_999_999,
do: {:error, "number is out of range"}
def in_english(0), do: {:ok, "zero"}
def in_english(number) do
{:ok,
number
|> Integer.digits()
|> Enum.reverse()
|> Enum.chunk_every(3)
|> Enum.with_index(fn element, index -> in_english_part(element, index) end)
|> Enum.reverse()
|> List.flatten()
|> Enum.reject(&is_nil/1)
|> Enum.join(" ")
}
end
@spec in_english_part([integer], integer) :: String.t()
defp in_english_part([0, 0, 0], _index), do: []
defp in_english_part(element, 3), do: [do_in_english_part(element) | ["billion"]]
defp in_english_part(element, 2), do: [do_in_english_part(element) | ["million"]]
defp in_english_part(element, 1), do: [do_in_english_part(element) | ["thousand"]]
defp in_english_part(element, 0), do: [do_in_english_part(element)]
defp do_in_english_part([d1, d2, d3]) when d3 != 0, do: [@in_english_19[d3], "hundred" | [do_in_english_part([d1, d2])]]
defp do_in_english_part([d1, d2, 0]), do: do_in_english_part([d1, d2])
defp do_in_english_part([d1, d2]) when d2 < 2, do: @in_english_19[Integer.undigits([d2, d1])]
defp do_in_english_part([0, d2]) when d2 > 1, do: @in_english_100[d2]
defp do_in_english_part([d1, d2]) when d2 > 1,
do: @in_english_100[d2] <> "-" <> @in_english_19[d1]
defp do_in_english_part([d1]), do: @in_english_19[d1]
end