I’ve just written the module below just as a practice. It converts numbers between different bases. But there is a problem. When converting from a non-decimal basis, the user can give a number with a digit that is greater than or equal to the base, which is invalid. So I want to validate inputs and return an error for such numbers. But I’m not quite sure how I should write it. I tried putting the following line after [h | t] = num
in to_decimal:
unless h < base do
:invalid
end
But weirdly, the function doesn’t return and continues execution! I also tried the following instead, so that at least I replace the invalid digit with the greatest valid digit:
unless h < base do
h = base-1
end
This had a weird outcome too. h goes back to its original value after the unless block!
So can anyone help?
Any other comment about the code is welcome.
Thanks
defmodule BaseConvert do
@symbols %{
0 => "0",
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
10 => "a",
11 => "b",
12 => "c",
13 => "d",
14 => "e",
15 => "f",
16 => "g",
17 => "h",
18 => "i",
19 => "j",
20 => "k",
21 => "l",
22 => "m",
23 => "n",
24 => "o",
25 => "p",
26 => "q",
27 => "r",
28 => "s",
29 => "t",
30 => "u",
31 => "v",
32 => "w",
33 => "x",
34 => "y",
35 => "z",
36 => "A",
37 => "B",
38 => "C",
39 => "D",
40 => "E",
41 => "F",
42 => "G",
43 => "H",
44 => "I",
45 => "J",
46 => "K",
47 => "L",
48 => "M",
49 => "N",
50 => "O",
51 => "P",
52 => "Q",
53 => "R",
54 => "S",
55 => "T",
56 => "U",
57 => "V",
58 => "W",
59 => "X",
60 => "Y",
61 => "Z"
}
@digits Map.to_list(@symbols) |> Enum.map(fn {k, v} -> {v, k} end) |> Map.new()
def convert(num, 10, to) do
num |> from_decimal(to) |> format()
end
def convert(num, from, to) when is_integer(num) do
num |> to_string() |> convert(from,to)
end
def convert(num, from, 10) do
num |> parse() |> to_decimal(from)
end
def convert(num, from, to) do
num |> parse() |> to_decimal(from) |> from_decimal(to) |> format()
end
defp from_decimal(num, base, result \\ []) when is_integer(num) do
result = [rem(num, base) | result]
unless num < base do
from_decimal(div(num, base), base, result)
else
result
end
end
defp to_decimal(num, base, result \\ 0) when is_list(num) do
unless length(num) == 0 do
[h | t] = num
result = result + :math.pow(base, length(num) - 1) * h
to_decimal(t, base, result)
else
trunc(result)
end
end
defp format(num) when is_list(num) do
Enum.map(num, fn x -> @symbols[x] end) |> List.to_string()
end
defp parse(num) do
String.split(num, "")
|> List.delete_at(-1)
|> List.delete_at(0)
|> Enum.map(fn x -> @digits[x] end)
end
end