If you want to not repeat yourself, you can inspire my Code Golf New Year Edition - An integer up to Roman numeral contribution .
-module(n2). -export([y/1]). -define(D(V,S),n(N)when N>=V->[??S|n(NV)];). y(N)->io:format(n(N)). ?D(1000,M)?D(900,CM)?D(500,D)?D(400,CD)?D(100,C)?D(90,XC)?D(50,L)?D(40,XL)?D(10,X)?D(9,IX)?D(5,V)?D(4,IV)?D(1,I)n(0)->[10].
It is not recommended and recommended to write code in erlang. Macros are bad. Avoid this if possible. It is difficult to debug, it introduces dependencies between intermodules that are not tracked by exchanging hot code, and so on. If you like the more functional approach βcode is data, data is codeβ, look at this as an example:
-module(roman). -compile([export_all]). toRoman(N) when is_integer(N), N >= 0 -> toRoman(N, [{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}]). toRoman(0, _) -> []; toRoman(N, [{X, V} | _] = S) when N >= X -> [V | toRoman(N - X, S)]; toRoman(N, [_ | S]) -> toRoman(N, S). test() -> F = fun (X) -> lists:flatten(toRoman(X)) end, "" = F(0), "I" = F(1), "III" = F(3), "VI" = F(6), "XXIII" = F(23), "XLIII" = F(43), "LXXV" = F(75), "LXXXVII" = F(87), "XIII" = F(13), "XXIII" = F(23), "MMMCMXCIX" = F(3999), "MMMCMXCVIII" = F(3998), "MMDXXXI" = F(2531), "CXL" = F(140), ok.
Just for curiosity, your code is about 5% faster in bytecode and 5% slower than mine. It performs one translation at 1.2us in bytecode and at 370 ns on the integrated Intel (R) Core (TM) 2 Duo T7500 @ 2.20GHz processor.
Change I did not use the recursive version of the tail, because the depth of the recursion is very small. Therefore, I was curious if he had any penalties or benefits from this. I canβt measure any in my bytecode algorithm even my own, but an interesting thing happens in the source code. If I wrote the original algorithm in a direct way (not optimized for tail calling), it is about 40% faster than mine in native code (one conversion in about 250 ns). There is no noticeable difference in the byte code. An interesting example is that tail call optimization is not worth doing.
toRoman(N) when N >= 1000 -> "M" ++ toRoman(N - 1000); toRoman(N) when N >= 900 -> "CM" ++ toRoman(N - 900); toRoman(N) when N >= 500 -> "D" ++ toRoman(N - 500); toRoman(N) when N >= 400 -> "CD" ++ toRoman(N - 400); toRoman(N) when N >= 100 -> "C" ++ toRoman(N - 100); toRoman(N) when N >= 90 -> "XC" ++ toRoman(N - 90); toRoman(N) when N >= 50 -> "L" ++ toRoman(N - 50); toRoman(N) when N >= 40 -> "XL" ++ toRoman(N - 40); toRoman(N) when N >= 10 -> "X" ++ toRoman(N - 10); toRoman(N) when N >= 9 -> "IX" ++ toRoman(N - 9); toRoman(N) when N >= 5 -> "V" ++ toRoman(N - 5); toRoman(N) when N >= 4 -> "IV" ++ toRoman(N - 4); toRoman(N) when N >= 1 -> "I" ++ toRoman(N - 1); toRoman(0) -> [].
PS . List antialiasing is not common with Erlang code. The return structure in the above examples is well known as io_list and is commonly accepted in the erlang io system. You can send it directly to sockets, ports, and so on. If you want to write it, for example, you can use io:put_chars(IOList) or io:format("Result: '~s'~n", [IOList]) .
EDIT2 . If the constant list is like the left operand ++ , the erlang compiler operator optimizes the list concatenation for you, therefore ["string" | L] ["string" | L] not required for speed. The resulting code is more readable, and the result is smoothed without penalty for performance. Personnel, if I am interested in performace, I would use this version, which is a bit repetitive, but the fastest that I know, and performs one conversion in 310ns in byte code and in 210ns in native.
toRoman(N) when N >= 1000 -> "M" ++ toRoman(N - 1000); toRoman(N) -> toRomanC(N div 100, N rem 100). toRomanC(0, N) -> toRomanX(N); toRomanC(1, N) -> "C" ++ toRomanX(N); toRomanC(2, N) -> "CC" ++ toRomanX(N); toRomanC(3, N) -> "CCC" ++ toRomanX(N); toRomanC(4, N) -> "CD" ++ toRomanX(N); toRomanC(5, N) -> "D" ++ toRomanX(N); toRomanC(6, N) -> "DC" ++ toRomanX(N); toRomanC(7, N) -> "DCC" ++ toRomanX(N); toRomanC(8, N) -> "DCCC" ++ toRomanX(N); toRomanC(9, N) -> "CM" ++ toRomanX(N). toRomanX(N) -> toRomanX(N div 10, N rem 10). toRomanX(0, N) -> toRomanI(N); toRomanX(1, N) -> "X" ++ toRomanI(N); toRomanX(2, N) -> "XX" ++ toRomanI(N); toRomanX(3, N) -> "XXX" ++ toRomanI(N); toRomanX(4, N) -> "XL" ++ toRomanI(N); toRomanX(5, N) -> "L" ++ toRomanI(N); toRomanX(6, N) -> "LX" ++ toRomanI(N); toRomanX(7, N) -> "LXX" ++ toRomanI(N); toRomanX(8, N) -> "LXXX" ++ toRomanI(N); toRomanX(9, N) -> "XC" ++ toRomanI(N). toRomanI(0) -> []; toRomanI(1) -> "I"; toRomanI(2) -> "II"; toRomanI(3) -> "III"; toRomanI(4) -> "IV"; toRomanI(5) -> "V"; toRomanI(6) -> "VI"; toRomanI(7) -> "VII"; toRomanI(8) -> "VIII"; toRomanI(9) -> "IX".