How to convert numbers to words in Erlang?


Keywords:erlang 


Question: 

I found this interesting question about converting numbers into "words":

Code Golf: Number to Words

I would really like to see how you would implement this efficiently in Erlang.


3 Answers: 

-module(int2txt).
-export([convert/1]).

convert(0) -> "zero";
convert(N) -> convert1(N).

convert1(0) -> "";
convert1(N) when N>=1000 ->
    join(convert_thou(thoubit(N), 0),convert1(N rem 1000));
convert1(N) when N>=100 ->
    join(convert1(N div 100),"hundred",convert1(N rem 100));
convert1(N) when N>=20 ->
    join(tens((N div 10)-1) ++"ty",convert1(N rem 10));
convert1(N) when N>=13 -> teens(N-12) ++ "teen";
convert1(N) -> ones(N).

convert_thou({0,0},_) -> "";
convert_thou({0,N2},C) -> convert_thou(thoubit(N2),C+1);
convert_thou({N,N2},C) -> join(convert_thou(thoubit(N2),C+1),
                   convert1(N),thouword(C)).

thoubit(N) -> {(N div 1000) rem 1000,N div 1000}.

ones(N) -> element(N,{"one","two","three","four","five","six","seven",
              "eight","nine","ten","eleven","twelve"}).
tens(N) -> element(N,{"twen","thir","for","fif","six","seven","eigh","nine"}).
teens(2) -> "four";
teens(N) -> tens(N+1).
thouword(0) -> "thousand";
thouword(C) -> illions(C) ++ "illion".
illions(N) -> element(N,{"m","b","tr","quadr","quint","sex","sept",
                         "oct","non","dec"}).

join(X,Y,Z) -> join(join(X,Y),Z).
join("",X) -> X;
join(X,"") -> X;
join(X,Y) -> X++" "++Y.

Tests:

1> int2txt:convert(0).
"zero"
2> int2txt:convert(1024).
"one thousand twenty four"
3> int2txt:convert(1048576).
"one million forty eight thousand five hundred seventy six"
4> int2txt:convert(1073741824).
"one billion seventy three million seven hundred forty one thousand
eight hundred twenty four"
5> int2txt:convert(1000001).   
"one million one"


int2text(Num) when is_integer(Num) -> int2text(integer_to_list(Num));
int2text(Num) ->
  Segs = segment(0, lists:reverse(Num)),
  Words = lists:reverse([Seg || Seg <- Segs, Seg =/= []]),
  string:join(Words, " ").

segment(Ths, "")          -> segment3(Ths, [$0,$0,$0]);
segment(Ths, [A])         -> segment3(Ths, [$0,$0,A]);
segment(Ths, [A,B])       -> segment3(Ths, [$0,B,A]);
segment(Ths, [A,B,C|Num]) -> segment3(Ths, [C,B,A]) ++ segment(Ths+1, Num).

segment3(_Ths, [$0,$0,$0]) -> [];
segment3(Ths,  [$0,A,B])   -> [thousands(Ths)] ++ segment2([A,B]);
segment3(Ths,  [A,B,C])    -> [thousands(Ths)] ++ segment2([B,C]) ++ ["hundred", ones([A])].

segment2([$0,A]) -> [ones([A])];
segment2([$1,A]) -> [ones([$1,A])];
segment2([A,B])  -> [ones([B]), tens([A])].

ones("0") -> "";
ones("1") -> "one";
...
ones("18") -> "eighteen";
ones("19") -> "nineteen".

tens("2") -> "twenty";
...
tens("9") -> "ninety".

thousands(0) -> "";
thousands(1) -> "thousand";
thousands(2) -> "million";
...


-module(num2word).
-export([num2word/1]).

num2word(0) ->
    "Zero";
num2word(N) when N < 10 ->
    lists:nth(N, ["One",   "Two", "Three", "Four", "Five",
                  "Six", "Seven", "Eight", "Nine"]);
num2word(N) when N < 20 ->
    lists:nth(N-9, [     "Ten",    "Eleven",   "Twelve",
                    "Thirteen",  "Fourteen",  "Fifteen",
                     "Sixteen", "Seventeen", "Eighteen",
                    "Nineteen"]);
num2word(N) when (N < 100) and ((N rem 10) =:= 0) ->
    lists:nth((N div 10)-1, ["Twenty",  "Thirty",  "Forty",  "Fifty",
                              "Sixty", "Seventy", "Eighty", "Ninety"]);
num2word(N) when N < 100 ->
    num2word((N div 10) * 10) ++ " " ++ num2word(N rem 10);
num2word(N) when ((N rem 100) =:= 0) ->
    {Scale, Name} = scale(N),
    Result = num2word(N div Scale) ++ " " ++ Name,
    case (N rem Scale) of
        0 -> Result;
        Remainder -> Result ++ " " ++ num2word(Remainder)
    end;
num2word(N) ->
    {Scale, _} = scale(N),
    num2word((N div Scale) * Scale) ++ " " ++ num2word(N rem Scale).

scale(N) when N < 1000 ->
    {100, "Hundred"};
scale(N) when N < 1000000 ->
    {1000, "Thousand"};
scale(N) when N < 1000000000 ->
    {1000000, "Million"};
scale(N) when N < 1000000000000 ->
    {1000000000, "Billion"};
scale(N) when N < 1000000000000000 ->
    {1000000000000, "Trillion"};
scale(N) when N < 1000000000000000000 ->
    {1000000000000000, "Quadrillion"};
scale(N) when N < 1000000000000000000000 ->
    {1000000000000000000, "Quintillion"}.